泛型概述
Java泛型(generics)是DK5中引入的一个新特性,泛型提供了编译时类型安全监测机制,该机制允许我们在编译时检测到非法的类型数据结构。
泛型的本质就是参数化类型,也就是所操作的数据类型被指定为一个参数。
如我们经常使用的ArrayList中的使用E表示泛型的形参,需要我们传递指定实际的类型。
如果不指定默认是Object类型,Object不能强转为其本身的子类型,所以出现java.lang.Object无法转换为java.lang.String。
泛型类和接口
泛型类
1、泛型类的定义语法
2、常用的泛型标识:T、E、K、V
泛型标识可以作为成员变量的类型定义标识和函数的返回类型标识和成员方法的参数类型标识。
class Test<T>{ //T创建对象的时候指定数据类型 private T a; public T getA(T b){ return a; } } //具体的实现如下: Test<Integer> t = new Test<>();//使用Integer类型指定 T 具体是什么
3、 泛型类相关知识
- 泛型类在创建对象的时候,来指定操作的具体类型
- 如果在创建对象的时候没有指定具体类型,将会按照Object类型操作。
- 泛型类不支持传入基本类型,因为传入的类型需要时Object的子类,在类型擦除的时候,如果没有extends,super,会默认把泛型参数变为Object,具体泛型擦除看下边
- 泛型类型在逻辑上可以看成是不同数据类型,但实际上事相同的类型
- 因为使用了泛型,我们可以在一个类中使用不同的数据类型如:Integer,String等
泛型子类
1、子类是泛型类
父类是泛型类,子类也是泛型类,子类中的泛型变量要有一个和父类的泛型要保持一致
class Test<T>{ private T a; public T getA(T b){ return a; } } class Son1<T,K> extends Test<T>{ //如果父类是泛型类,子类要有一个泛型变量和父类保持一致 } class Son2<K> extends Test<T>{ //报错:不保持一致会报错 }
2、子类不是泛型类
子类不是泛型类,父类要明确泛型的数据类型
class Test<T>{ private T a; public T getA(T b){ return a; } } class Son1 extends Test<String>{ //子类不是泛型类,父类要明确泛型的数据类型 } class Son2 extends Test<T>{ //报错:需要明确父类的泛型数据类型 }
泛型接口
泛型接口的定义语法
interface Test<T>{ T get(T a); } class Son1<T,K> implements Test<T>{ @Override public T get(T a) { return null; } } class Son2 implements Test<String>{ @Override public String get(String a) { return null; } } class Son3 implements Test{//如果不写的话,默认的T就会变成Object @Override public Object get(Object a) { return null; } }
- 实现类不是泛型类,接口要明确数据类型
- 实现类也是泛型类,实现类和接口的泛型类型要一致
泛型方法
- pub1ic与返回值中间非常重要,可以理解为声明此方法为泛型方法。
- 只有声明了的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。
- 表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T.
- 与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型。
- 反向方法支持静态,但是泛型类中的成员方法不支持静态static的使用
举例如下:
class Test1{ public <T> T geta(T a){ return a; } } public class Main { public static void main(String[] args) { Test1 test1 = new Test1(); Integer geta = test1.geta(1);//根据传入参数的类型指定泛型形参 } }
反向方法的可变参数
泛型方法总结:
- 泛型方法能使方法独立于类而产生变化
- 如果static方法要使用泛型能力,就必须使其成为泛型方法
类型通配符
类型通配符上限
类型通配符一般是使用?代替具体的类型参数,所以类型通配符是类型实参.
? extends Number,表示的是类型通配符的上线是Number
class Test1<T>{ public void get1(){ } } public class Main{ public static void main(String[] args) { get(new Test1<>()); get1(new Test1<Integer>()); } public static void get(Test1<?> test1){ test1.get1(); } public static void get1(Test1<? extends Number> test1){ test1.get1(); } }
类型通配符下限
采用super关键字 ? super Number表示类型最低是Number
class Test1<T>{ public void get1(){ } } public class Main{ public static void main(String[] args) { get(new Test1<>()); get1(new Test1<Number>()); } public static void get(Test1<?> test1){ test1.get1(); } public static void get1(Test1<? super Number> test1){ test1.get1(); }
类型擦除
无限制的类型擦除
有限制的类型擦除
擦除方法中类型定义的参数
桥接方法
因为在编译的时候,为了保证实现类重写了接口的方法,虽然实现类有了Integer的方法,但是在泛型擦除的时候会在实现类中生成一个Object的方法。
泛型与数组
泛型数组的创建
- 可以声明带泛型的数组引用,但是不能直接创建带泛型的数组对象
- 可以通过java.lang.reflect.Array的newInstance(Class,int)创建T[]数组
- 方法一
public static void main(String[] args) { ArrayList<String>[] a = new ArrayList<String>[4]; //错误案例 ArrayList<String>[] b = new ArrayList[4];//创建数组 }
- 方法二
class Test1<T>{ private T[] arr; public Test1(Class<T> clz,int len){ //创建数组 arr = (T[]) Array.newInstance(clz,len); } public void put(int index, T item){ arr[index]=item; } }
泛型与反射
public static void main(String[] args) throws Exception { Class test1Class= Test1.class; Constructor constructor = test1Class.getConstructor(); Object o = constructor.newInstance(); }
反射常用的泛型类
Class<T> Constructor<T>