Java 泛型
Java SE 5.0中增加了泛型的机制。
1、泛型:类型参数化
为什么要使用泛型?使编写的代码可以被不同类型的对象重用,避免强制转换导致的错误;
比如:想往一个数组中添加字符串和整数。
2、ArrayList是一个泛型程序设计的实例
类型参数: ArrayList<String> files=new ArrayList<>()
省略的类型可以从前面的类中推出String。
files 在使用get方法时,编译器知道files的类型为String类
(出现编译错误比类在运行时出现类的强制转换要好得多)
3、泛型特点:泛型只在编译阶段有效。看下面的代码:
List<Integer> integerArrayList = new ArrayList<Integer>();
Class classStringArrayList = stringArrayList.getClass();
Class classIntegerArrayList = integerArrayList.getClass();
if(classStringArrayList.equals(classIntegerArrayList)){
Log.d("泛型测试","类型相同");
}
输出结果:D/泛型测试: 类型相同。
通过上面的例子可以证明,在编译之后程序会采取去泛型化的措施。也就是说Java中的泛型,只在编译阶段有效。在编译过程中,正确检验泛型结果后,会将泛型的相关信息擦出,并且在对象进入和离开方法的边界处添加类型检查和类型转换的方法。也就是说,泛型信息不会进入到运行时阶段。
对此总结成一句话:泛型类型在逻辑上看以看成是多个不同的类型,实际上都是相同的基本类型。
4、泛型程序设计的难处:使用类型参数时当然希望能内置所有的类,用少量代码实现大多数功能。所以要预测所用类的所有可能用途
如果想要将ArrayList<Manager>中所有元素添加到ArrayList<Empolyee>中,反过来则不行。要使用通配符(wildcard type)
泛型程序设计有三个能力级别,使用泛型是最基础的,但出现问题就要系统学习泛型来解决问题。
5、简单泛型类
一般格式:
class 类名称 <泛型标识:可以随便写任意标识号,标识指定的泛型的类型>{ private 泛型标识 /*(成员变量类型)*/ var; ..... } }
public class GenericClassTest { public static void main(String args[]) { Pair<String> p=new Pair<>(); System.out.print(p.toString()); } } class Pair<T>{ private T first; private T second; public Pair() { first=null;second=null; } public Pair(T first,T second){ this.first=first; this.second=second; } public T getFirst() { return first; } public T getSecond() { return second; } public void setFirst(T first) { this.first=first; } public void setSecond(T second) { this.second=second; } }
Pair<Integer> p=new Pair<>(1,2); System.out.print(p.getFirst()+p.getSecond());//输出3
E 表示集合的元素类型,k、v表示键值,T(U\S)表示任意类型
用具体的类型替换类型变量户也可以实例化泛型类型
(泛型类可以看做是普通类的工厂)
6、泛型方法
public class GenericMethodTest { public static void main(String args[]) { InsClass instanceT=new InsClass(); System.out.print(instanceT.<Integer>getLast(1,2,3)); } } class InsClass{ public<T> T getLast(T...a) { //在修饰符后,返回类型前声明<>类型参数。返回类型为T return a[a.length-1]; } }
7泛型接口
public interface Generator<T> { T test(); }
public class FruitGenerator<T> implements Generator<T>{ //泛型类 private T t; public T test(){ return t; } } class WaterGenerator implements Generator<String>{ //传入泛型实参的类 public String test() { return "gaga"; } }
8、Class<T>是什么?
Class<E> 是指“泛类”,不指定类,而在实际运行过程中给定具体的Class。(常用于反射)
下面的例子说明:Class<E>描述的是eclass 是E类本身而不是E类的实例。
InstantiationException ,
IllegalAccessException 是两个必须实现的Exception
public class Tmethod<E> { public <T> E gMethod(Class<E> eclass,Class<T> tclass)throws InstantiationException , IllegalAccessException {
E ins=eclass.newInstance(); //eclass是Class<E>,在运行中指定类,由类生成实例,赋给ins这个E型 return ins; } }
9、泛型通配符<?> [转载自:https://www.cnblogs.com/coprince/p/8603492.html (java 泛型详解-绝对是对泛型方法讲解最详细的,没有之一)]
Ingeter是Number的一个子类,同时也验证过Generic<Ingeter>与Generic<Number>实际上是相同的一种基本类型。
那么问题来了,在使用Generic<Number>作为形参的方法中,能否使用Generic<Ingeter>的实例传入呢?在逻辑上类似于Generic<Number>和Generic<Ingeter>是否可以看成具有父子关系的泛型类型呢?
为了弄清楚这个问题,我们使用Generic<T>这个泛型类继续看下面的例子:
public void showKeyValue1(Generic<Number> obj){
Log.d("泛型测试","key value is " + obj.getKey());
}
Generic<Integer> gInteger = new Generic<Integer>(123); Generic<Number> gNumber = new Generic<Number>(456); showKeyValue(gNumber); // showKeyValue这个方法编译器会为我们报错:Generic<java.lang.Integer> // cannot be applied to Generic<java.lang.Number> // showKeyValue(gInteger);
通过提示信息我们可以看到Generic<Integer>不能被看作为`Generic<Number>的子类。
由此可以看出:同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的。
回到上面的例子,如何解决上面的问题?总不能为了定义一个新的方法来处理Generic<Integer>类型的类,这显然与java中的多台理念相违背。因此我们需要一个在逻辑上可以表示同时是Generic<Integer>和Generic<Number>父类的引用类型。由此类型通配符应运而生。
我们可以将上面的方法改一下:
public void showKeyValue1(Generic<?> obj){
Log.d("泛型测试","key value is " + obj.getKey());
}
类型通配符一般是使用?代替具体的类型实参,注意了,此处’?’是类型实参,而不是类型形参 。重要说三遍!此处’?’是类型实参,而不是类型形参 ! 此处’?’是类型实参,而不是类型形参 !再直白点的意思就是,此处的?和Number、String、Integer一样都是一种实际的类型,可以把?看成所有类型的父类。是一种真实的类型。
可以解决当具体类型不确定的时候,这个通配符就是 ? ;当操作类型时,不需要使用类型的具体功能时,只使用Object类中的功能。那么可以用 ? 通配符来表未知类型。
10、类型变量的限定
如果在泛型方法内部有“compareTo”的方法,那么如何保证T是comparabl的呢?
public static <T extends Comparable> T method(T[] a)
将T设置限定(bound),限制T必须是继承Comparable类的类型。

浙公网安备 33010602011771号