一:泛型简介
1 泛型简介
1.1 泛型的基本概念
- 泛型的本质就是“数据类型的参数化”----------把类型当作是参数一样传递,数据类型只能是引用类型
- 可以把泛型理解为数据类型的占位符(类似形式参数),即告诉编译器,在调用泛型时必须传入的实际类型。
- 这种参数类型可以用在类,接口和方法中,分别被成为泛型类,泛型接口,泛型方法
1.2 泛型的好处
- 代码可读性更好(没有泛型就得使用Object类型来实现任意参数类型,使用的时候需要进行类型转换,instanceof方法)
- 程序更加安全(只要编译时期没有警告,运行时期就不会出现ClassCastException异常)
1.3 类型擦除
- 类型擦除:编码时使用泛型写的类型参数,编译器会在编译时去掉,这称之为类型擦除
- 泛型参数在编译后被替换成Object,运行时虚拟机并不知道泛型
泛型主要用于编译阶段,编译后生成的字节码文件不包含泛型中的类型信息,设计类型转换仍然是普通的强制类型转换
2 泛型的使用
2.1 定义泛型
泛型字符可以是任何字符,一般采用:E,T,K,V,N,?
泛型标记 | 对应单词 | 说明 |
---|---|---|
E | Element | 在容器中使用,表示容器中的元素 |
T | Type | 表示普通的Java类 |
K | Key | 表示键 |
V | Value | 表示值 |
N | Number | 表示数值类型 |
? | 表示不确定的java类型 |
2.2 泛型类
泛型类就是把泛型定义在类上,用户使用到该类的时候才把类型明确下来。
泛型类的具体使用方法:在类的名称后添加一个或多个类型参数声明,如,<T,K,V>
2.2.1 语法结构
public class 类名<泛型表示符号>
2.2.2 示例
public class Test {
public static void main(String[] args) {
Demo3<String> demo3 = new Demo3<>();
demo3.setFlag("admin");
String flag = demo3.getFlag();
System.out.println(flag);
}
}
2.3 泛型接口
2.3.1 语法结构
public interface 接口名<泛型表示符号>
2.3.2 示例
public interface Demo4<T> {
public T getName(T name);
}
public class Demo5 implements Demo4<String>{
@Override
public String getName(String name) {
return name;
}
}
Demo5 demo5 = new Demo5();
String name = demo5.getName("张三");
System.out.println(name);
Demo4<String> demo4 = new Demo5();
String name1 = demo4.getName("李四");
System.out.println(name1);
Demo4 demo4 = new Demo5(); // 接口指向实现类
String name1 = (String)demo4.getName("李四"); //如果没有指定类型,将是Object类型
System.out.println(name1);
2.4 泛型方法
泛型类中所定义的泛型,在方法中也可以使用,但是我们经常需要使用仅仅在某一个方法上使用泛型,这个时候可以使用泛型方法。
泛型方法:是指将方法的类型参数定义成泛型,以便在调用时接受不同类型的参数。类型参数可以有多个,如<k,v>,定义时,类型参数一般放到返回值前面。
调用泛型方法时不需要像泛型类一样告诉编译器是什么类型,编译器可以自动推断出类型。
2.4.1 非静态方法
2.4.1.1 语法结构
public <泛型表示符号> void getName(泛型表示符号 name){}
public <泛型表示符号> 泛型表示符号 getName(泛型表示符号 name){}
2.4.1.2 示例
public <T> void setName(T name){
System.out.println(name);
}
public <T> T getName(T name){
return name;
}
2.4.2 静态方法
静态方法需要注意的点:不能使用类上定义的泛型(因为静态方法在对象之前加载)
2.4.2.1 语法结构
public abstract <泛型表示符号> void getName(泛型表示符号 name){}
public abstract <泛型表示符号> 泛型表示符号 getName(泛型表示符号 name){}
2.4.2.2 示例
public abstract <T> void setName(T name){
System.out.println(name);
}
public abstract <T> T getName(T name){
return name;
}
2.4.3 泛型方法和可变参数
在泛型方法中,泛型也可以定义成可变参数
2.4.3.1 语法结构
public <泛型表示符号> void showMsg(泛型表示符号 ... args){}
2.4.3.2 示例
public <T> void method(T...args){
for (T t:args){
System.out.println(t);
}
}
MethodGeneric methodGeneric1 = new MethodGeneric();
String[] arr = new String[]{"a","b","c"};
methodGeneric1.method(arr);
2.5 通配符和上下限定
- 通配符只能用在定义变量的时候,不能用在泛型类上,同下限限定
- 可以限定类型
- 没有做限定的时候,泛型定义的变量只能使用Object的方法,因为编译器不知道它的具体类型,使用限定过的泛型就可以使用其父类或者父接口的方法了。
2.5.1 无界通配符(可以表示任意类型)
“?” 表示类型通配符,用于替代具体的类型,它只能在<>中使用,可以解决当具体类型不确定的问题。这样就可以不先确定泛型类的类型。
2.5.1.1 语法结构
public void showFlag(Generic<?> generic){}
2.5.1.2 示例
public class showMsg {
public void showFlag(Demo3<?> demo3){
System.out.println(demo3.getFlag());
}
}
showMsg showMsg = new showMsg();
Demo3<String> demo31 = new Demo3<>();
demo3.setFlag("张三");
showMsg.showFlag(demo3);
2.5.2 通配符的上限限定
上限限定表示通配符的类型是T类以及T类的子类或者T接口以及T接口的子接口。
该方式同样使用于泛型的上限限定。
2.5.2.1 语法结构
public void showFlag(Generic<? extends Number> generic){}
2.5.2.2 示例
public void showFlag(Demo3<? extends Number> demo3){
System.out.println(demo3.getFlag());
}
showMsg showMsg = new showMsg();
Demo3<Number> demo31 = new Demo3<>();
demo31.setFlag(123);
showMsg.showFlag(demo31);
2.5.3 通配符的下限限定
下限限定表示通配符的类型是T类以及T类的父类或者T接口以及T接口的父接口。
注意:该方法不适用泛型类
2.5.3.1 语法结构
public void showFlag(Generic<? super Integer> generic)
2.5.3.2 示例
public void showFlag(Demo3<? super Integer> demo3){
System.out.println(demo3.getFlag());
}
showMsg showMsg = new showMsg();
Demo3<Number> demo31 = new Demo3<>();
demo31.setFlag(123);
showMsg.showFlag(demo31);
3 泛型总结
泛型用于编译阶段
类型参数在编译后被替换成Object,运行时虚拟机并不知道泛型
- 基本类型不能用于泛型,可以使用其包装类
- 不能通过类型参数创建对象
T elm = new T (); // 不支持