1.泛型的定义
Java泛型(Generic)是J2SE1.5中引入的一个新特性,其本质是参数化类型,也就是说操作的数据类型被指定为一个参数(Type Paramcter) 这种参数类型可以用在类,接口和方法的创建中
泛型类:public class Demo<T> {} ,T表示未知类型。
泛型接口:public interface ImplDemo<T,V>{} ,和定义类一样(接口就是一个特殊类)。
泛型方法:public <T> void demo1(T name){System.out.println(name);} , public <T> T demo2(T t){ return t;}
2.桥接方法和类型擦除
泛型的类型擦除(泛型只在编译阶段有效,泛型类型在逻辑上可看成是多个不同的类型,但是其实质都是同一个数据类型,编译之后程序会采取去泛型化的措施)
问题一:list.getClass() == strList.getClass() 输出的结果是什么?
@Test
public vodi m01() {
List list = new ArrayList();
List<String> strList = new ArrayList<>();
System.out.println(list.getClass() == strList.getClass());
}
答案:输出的结果是true。直接打开命令行工具,使用javap varbose方法显示字节码文件,可直接看出是一样的
问题二:在编译之后使用反射重新非指定的List类型是否可以?
@Test
public void m02() throw Exception {
List<String> strList = new ArrayList<>();
strList.add("abc"); // 直接添加指定的同类型数据可行
strList.add("abcd");
strList.add(new Object()) // 直接添加不同指定类型的数据不可行,Object为add方法指定的类型
System.out.println("strList.size" + strList.size());
System.out.println("----------------------------");
// 在编译完之后, 使用反射添加非指定类型数据
Class<? extends List> clazz = strList.getClass();
Method addMethod = clazz.getDeclaredMethod("add",Object.class);
addMethod.invoke(strList,new Object()); // 可行
System.out.println(strList.size());
}
问题三:堆污染:当一个可变泛型参数指向一个无泛型参数时,可能发生的一种现象
public class Pollution {
public static void main(String args[]) {
Set set = new Set();
// set.add("abc");
varMethod(set);
Iteartor<String> iteartor = set.Iteartor();
while(iteartor.hasNext()) {
String str = iteartor.next();
}
}
public static void varMethod(Set<Integer> set) {
set.add(123);
System.out.println(set.size());
}
}
答案:会产生堆污染
桥接方法(JDK1.5引入泛型后,为了使Java的泛型方法生成的字节码和1.5版本前的字节码相兼容,由编译器自动生成的方法。是伴随着泛型方法一起出现的,目的是父类实现泛型方法,子类实现具体类型的同一方法,在多态的时候能够实现子类具体类型的方法)
public interface SuperClass<T> {
T m01(T param);
}
// 实现泛型接口
public class SubClass implements SuperClass<String> {
public String m01(String param) {return param+"---";}
}
public class BridgeMethodTest {
public static void main(String[] args) {
SuperClass clazz = new SubClass() // 多态,父类的引用指向子类的实现
System.out.println("123abc"); // 能编译,能执行
System.out.println(new Object()); // 能编译,不能执行(因为桥接方法的调用还是类中的String类型的m01方法)
Method m = clazz.getClass().getDeclaredMethod("mo1",Object.class);
System.out.println(m.isBridge());
}
}
控制台使用javap varbose 命令显示SubClass字节码,会显示有两个m01方法,下面这个是泛型的桥接方法,还有一个是类中的String类型m01方法
3.通配符
泛型的通配符包含三种边界:
无界:?<?>
上界:<? extends E>
下界:<? super E>
泛型的作用:用于编译检查,保证类型的安全,避免强制转换的硬编码,增加调用代码的重用性
4.泛型的使用
泛型的具体使用:
泛型和成员属性一样,需要先声明才能使用,泛型的声明采用<> 进行声明,声明一般约定采用单个大写字母表示,常用的用K E T V 等等
泛型类
泛型方法:
实体方法可以使用在类中定义的泛型或者方法中定义的泛型
静态方法不可以使用在类中定义的泛型,只能使用在静态方法上定义的泛型
泛型接口