黑马程序员_JDk1.5新特性之_泛型

来源:互联网 发布:化工行业发展现状知乎 编辑:程序博客网 时间:2024/05/18 00:37

------- android培训、java培训、期待与您交流! ----------

泛型是JDK1.5中引入的一个新特性(很重要),允许在定义类和接口的时候使用类型参数。声明的类型参数在使用时用具体的类型来替换。泛型最主要的应用是在JDK1.5中的新集合类框架中。

JDK1.5以前的集合类中存在什么问题
 ArrayList collection = new ArrayList();
 collection.add(1);
 collection.add(1L);
 collection.add("abc");
 int i = (Integer) collection.get(1);//编译要强制类型转换且运行时出错!
JDK1.5的集合类希望你在定义集合时,明确表示你要向集合中装哪种类型的数据,无法加入指定类型以外的数据.
ArrayList<Integer> collection2 = new ArrayList<Integer>();
collection2.add(1);
/*collection2.add(1L);
collection2.add(“abc”);*/

//这两行代码编译时就报告了语法错误
int i2 = collection2.get(0);//不需要再进行类型转换
泛型是提供给javac编译器使用的,可以限定集合中的输入类型,让编译器挡住源程序中的非法输入,编译器编译带类型说明的集合时会去除掉“类型”信息,使程序运行效率不受影响,对于参数化的泛型类型,getClass()方法的返回值和原始类型完全一样。由于编译生成的字节码会去掉泛型的类型信息,只要能跳过编译器,就可以往某个泛型集合中加入其它类型的数据,例如,用反射得到集合,再调用其add方法即可。

泛型中使用到的术语

ArrayList<T>类定义和ArrayList<Integer>类引用:

整个ArrayList<T>称为泛型类型

ArrayList<T>中的T称为类型变量或类型参数

整个ArrayList<Integer>称为参数化的类型

ArrayList<Integer>中的Integer称为类型参数的实例或实际类型参数

ArrayList<Integer>中的<>读作typeof

ArrayList称为原始类型

泛型使用的细节问题:

参数化类型与原始类型的兼容性:

1.参数化类型可以引用一个原始类型的对象(报警告,不报错)

  Collection<String> c = new Vector();//编译器来决定是否通过

2.原始类型可以引用一个参数化类型的对象(报警告,不报错)

   Collection c = new Vector<String>();//原来的方法接受一个集合参数,新的类型也要能传进去

参数化类型不考虑类型参数的继承关系:

1.Vector<String> v = new Vector<Object>();//错误

如果不写<Object>没错,Object是任意对象,而String只接受String类型,不符。

2.Vector<Object> v = new Vector<String>();//同理,也错误

编译器不允许创建泛型变量的数组。即在创建数组实例时,数组的元素不能使用参数化的类型。

Vector<Integer> vectorList[] = new Vector<Integer>[10];//这样是错误的

但是这样是不报错的:

Vector v1 = new Vector<String>(); //原始类型可以引用一个参数化类型的对象(报警告,不报错)
Vector<Object> v = v1;//.参数化类型可以引用一个原始类型的对象(报警告,不报错)

编译器是一行一行的扫描而不是连起来看

泛型中的通配符?

定义一个方法,该方法用于打印出任意参数化类型的集合中的所有数据,该方法如何定义呢?

强调:使用?通配符可以引用其他各种参数化的类型,?通配符定义的变量主要用作引用,可以调用与参数化无关的方法,不能调用与参数化有关的方法。

限定通配符的上边界:

Integer必须是Number 的父类
正确:Vector<? extends Number> x = new Vector<Integer>();
错误:Vector<? extends Number> x = new Vector<String>();
限定通配符的下边界:

Number的父类必须是Integer,Byte与Integer是平级的不行。
正确:Vector<? super Integer> x = new Vector<Number>();
错误:Vector<? super Integer> x = new Vector<Byte>();

由于Set接口实现了Iterable<E>接口,所以对元素进行迭代还要将Map变成Set。

强调一点:只有引用类型才能作为泛型方法的实际参数。

  swap(new String[]{"abc","xyz","itcast"},1,2);
     //int是基本类型,不是引用类型

    // swap(new int[]{1,3,5,6},2,3);//语句会报告编译错误。

我们如何自己定义泛型方法

用于放置泛型的类型参数的尖括号应出现在方法的其他所有修饰符之后和在方法的返回类型之前,也就是紧邻返回值之前。按照惯例,类型参数通常用单个大写字母表示。

普通方法、构造方法和静态方法中都可以使用泛型。
也可以用类型变量表示异常,称为参数化的异常,可以用于方法的throws列表中,但是不能用于catch子句中。
在泛型中可以同时有多个类型参数,在定义它们的尖括号中用逗号分开:
public static <K,V> V getValue(K key) { return map.get(key);}

例题:编写一个泛型方法,自动将Object类型的对象转换成其他类型。

自动将obj转换成String类型的

泛型类型参数的类型推断:判断泛型方法的实际类型参数

1.根据调用泛型方法时实际传递的参数类型或返回值的类型来推断

当某个类型变量只在整个参数列表中的所有参数和返回值中的一处被应用了,那么根据调用方法时该处的实际应用类型来确定,这很容易凭着感觉推断出来,即直接根据调用方法时传递的参数类型或返回值来决定泛型参数的类型,例如:
  swap(new String[3],3,4)       static <E> void swap(E[] a, int i, int j)

这里为String

2.当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型都对应同一种类型来确定,这很容易凭着感觉推断出来,例如:
  add(3,5)    static <T> T add(T a, T b)

3.当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型对应到了不同的类型,且没有使用返回值,这时候取多个参数中的最大交集类型,例如,下面语句实际对应的类型就是Number了,编译没问题,只是运行时出问题:
  fill(new Integer[3],3.5f)    static <T> void fill(T[] a, T v)

(形象点:最大公倍数)

4.当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型对应到了不同的类型, 并且使用返回值,这时候优先考虑返回值的类型,例如,下面语句实际对应的类型就是Integer了,编译将报告错误,将变量x的类型改为float,对比eclipse报告的错误提示,接着再将变量x类型改为Number,则没有了错误:
  int x =(3,3.5f)    static <T> T add(T a, T b)

5.参数类型的类型推断具有传递性,下面第一种情况推断实际参数类型为Object,编译没有问题,而第二种情况则根据参数化的Vector类实例将类型变量直接确定为String类型,编译将出现问题:
 copy(new Integer[5],new String[5])  static <T> void copy(T[] a,T[]  b);
 copy(new Vector<String>(), new Integer[5])  static <T> void copy(Collection<T> a , T[] b);

类级别的泛型

如果类的实例对象中的多处都要用到同一个泛型参数,即这些地方引用的泛型类型要保持同一个实际类型时,这时候就要采用泛型类型的方式进行定义,也就是类级别的泛型。

当一个变量被声明为泛型时,只能被实例变量、方法和内部类调用,而不能被静态变量和静态方法调用。因为静态成员是被所有参数化的类所共享的,所以静态成员不应该有类级别的类型参数
但是这样是可以的:

public static <T> void update2(T obj){
  
 }

通过反射获得泛型的参数化类型

----------------------- android培训、java培训、java学习型技术博客、期待与您交流! ----------------------

详情请查看:http://edu.csdn.net/heima


 

原创粉丝点击