定义:
泛型,即“参数化类型”。就是将所操作的数据类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定 义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。
使用:
1、泛型类
泛型类,是在实例化类的时候指明泛型的具体类型;
//此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
//在实例化泛型类时,必须指定T的具体类型
public class ClassName<T>{
//data这个成员变量的类型为T,T的类型由外部指定
//泛型类中的成员变量不能用static修饰,因为静态变量无需创建类即可调用,无法确定是那种类型,编译不能通过
//即 public static T t; 会编译报错
private T data;
public ClassName(T data) {//泛型构造方法的形参data类型为T
this.data = data;
}
public T getData() {//此方法返回值类型为T
return data;
}
public void setData(T data) {
this.data = data;
}
}
定义的泛型类,不一定要传入泛型类型实参。
在使用泛型的时候如果传入泛型实参,则会根据传入的泛型实参做相应的限制,此时泛型才会起到本应起到的限制作用。
如果不传入泛型类型实参的话,在泛型类中使用泛型的方法或成员变量定义的类型可以为任何的类型。
ClassName className= new ClassName ("111111"); ClassName className1 = new ClassName(true); ClassName className2 = new ClassName(333.3); ClassName className3 = new ClassName(000000);
泛型的类型参数只能是类类型,不能是简单类型。
2、泛型接口
//定义一个泛型接口 public interface InterfaceName<T> { T value(); }
当实现泛型接口的类,传入泛型实参时:
public class Name implements InterfaceName<String> { @Override public String value() { return null; } }
当实现泛型接口,未传入泛型实参时,与泛型类的定义相同,在声明类的时候,需将泛型的声明也一起加到类中 ,如下:
public class Name<T> implements InterfaceName<T> { @Override public T value() { return null; } }
3、泛型方法
泛型方法,是在调用方法的时候指明泛型的具体类型
public class ClassName { //public 与 返回值中间<T>非常重要,可以理解为声明此方法为泛型方法。 //只有声明了<T>的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。 //<T>表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。 public <T>T say(T t) { System.out.println("111"); } public static <E> test(E e) { System.out.println("111"); } }
//虽然在方法中使用了泛型,但是这并不是一个泛型方法。 //这只是类中一个普通的成员方法,只不过他的返回值是在声明泛型类已经声明过的泛型。 //所以在这个方法中才可以继续使用 T 这个泛型。 public T getKey(){ return key; }
//泛型的数量也可以为任意多个, 如: public <T,K> K show(TestClass<T> testClass){ ... }
//这个方法是有问题的,编译器会为我们提示错误信息:"UnKnown class 'E' " //虽然我们声明了<T>,也表明了这是一个可以处理泛型的类型的泛型方法。 //但是只声明了泛型类型T,并未声明泛型类型E,因此编译器并不知道该如何处理E这个类型。 public <T> T test01(ClassName<E> className){ ... } //这个方法也有问题的,编译器会为我们提示错误信息:"UnKnown class 'T' " //对于编译器来说T这个类型并未项目中声明过,因此编译也不知道该如何编译这个类。 //所以这也不是一个正确的泛型方法声明。 public void test02(T t){ }
在泛型方法中,自己定义的泛型变量,与类无关;
泛型方法的一些介绍可以再参考此博文:https://blog.csdn.net/s10461/article/details/53941091
泛型通配符、上下边界:
类型通配符是使用?代替方法具体的类型实参。
为泛型添加上边界,即传入的类型实参必须是指定类型的子类型:<? extends Super>
为泛型添加下边界,即传入的类型实参必须是指定类型的父类型:<? super child>
<?>指定了没有限制的泛型类型
类型擦除:
泛型有一个特性,就是只在编译时有效,运行时无效,原因就是:
编译阶段会进行所谓的“类型擦除”(Type Erasure),将所有的泛型表示(尖括号中的内容)都替换为具体的类型(其对应的原生态类型)
- 消除类型参数声明,即删除<>及其包围的部分。
- 根据类型参数的上下界推断并替换所有的类型参数为原生态类型:如果类型参数是无限制通配符或没有上下界限定则替换为Object,如果存在上下界限定则根据子类替换原则取类型参数的最左边限定类型(即父类)。
- 为了保证类型安全,必要时插入强制类型转换代码。
- 自动产生“桥接方法”以保证擦除类型后的代码仍然具有泛型的“多态性”。
例:
- List<String>、List<T> 擦除后的类型为 List。
- List<String>[]、List<T>[] 擦除后的类型为 List[]。
- List<? extends E>、List<? super E> 擦除后的类型为 List<E>。
- List<T extends Serialzable & Cloneable> 擦除后类型为 List<Serializable>。
参考:
https://blog.csdn.net/s10461/article/details/53941091
https://www.cnblogs.com/joeblackzqq/p/10813143.html
https://www.cnblogs.com/drizzlewithwind/p/6101081.html
posted on
浙公网安备 33010602011771号