《Java编程思想》学习笔记8——泛型编程高级

1.泛型边界:

Java泛型编程时,编译器忽略泛型参数的具体类型,认为使用泛型的类、方法对Object都适用,这在泛型编程中称为类型信息檫除。

例如:

[java]  view plain copy
  1. class GenericType{  
  2.     public static void main(String[] args){  
  3.         System.out.println(new ArrayList<String>().getClass());  
  4.         System.out.println(new ArrayList<Integer>().getClass());  
  5. }  
  6. }  

输出结果为:

java.util.ArrayList

java.util.ArrayList

泛型忽略了集合容器中具体的类型,这就是类型檫除。

但是如果某些泛型的类/方法只想针对某种特定类型获取相关子类应用,这时就必须使用泛型边界来为泛型参数指定限制条件。

例如:

[java]  view plain copy
  1. interface HasColor{  
  2.     java.awt.Color getColor();  
  3. }  
  4. class Colored<T extends HasColor>{  
  5.     T item;  
  6.     Colored(T item){  
  7.         this.item = item;  
  8. }  
  9. java.awt.Color color(){  
  10.     //调用HasColor接口实现类的getColor()方法  
  11.     return item.getColor();  
  12. }  
  13. }  
  14. class Dimension{  
  15.     public int x, y, z;  
  16. }  
  17. Class ColoredDimension<T extends Dimension & HasColor>{  
  18.     T item;  
  19.     ColoredDimension(T item){  
  20.         this.item = item;  
  21. }  
  22. T getItem(){  
  23.     return item;  
  24. }  
  25. java.awt.Color color(){  
  26.     //调用HasColor实现类中的getColor()方法  
  27.     return item.getColor();  
  28. }  
  29. //获取Dimension类中定义的x,y,z成员变量  
  30. int getX(){  
  31.     return item.x;  
  32. }  
  33. int getY(){  
  34.     return item.y;  
  35. }  
  36. int getZ(){  
  37.     return item.z;  
  38. }  
  39. }  
  40. interface Weight{  
  41.     int weight();  
  42. }  
  43. class Solid<T extends Dimension & HasColor & Weight>{  
  44.     T item;  
  45.     Solide(T item){  
  46.         this.item = item;  
  47. }  
  48. T getItem(){  
  49.     return item;  
  50. }  
  51. java.awt.Color color(){  
  52.     //调用HasColor实现类中的getColor()方法  
  53.     return item.getColor();  
  54. }  
  55. //获取Dimension类中定义的x,y,z成员变量  
  56. int getX(){  
  57.     return item.x;  
  58. }  
  59. int getY(){  
  60.     return item.y;  
  61. }  
  62. int getZ(){  
  63.     return item.z;  
  64. }  
  65. int weight(){  
  66.     //调用Weight接口实现类的weight()方法  
  67.     return item.weight();  
  68. }  
  69. }  
  70. class Bounded extends Dimension implements HasColor, Weight{  
  71.     public java.awt.Color getColor{  
  72.         return null;  
  73. }  
  74. public int weight(){  
  75.     return 0;  
  76. }  
  77. }  
  78. public class BasicBounds{  
  79.     public static void main(String[] args){  
  80.         Solid<Bounded> solid = new Solid<Bounded>(new Bounded());  
  81.         solid.color();  
  82.         solid.getX();  
  83.         solid.getY();  
  84.         solid.getZ();  
  85.         solid.weight();  
  86. }  
  87. }  

Java泛型编程中使用extends关键字指定泛型参数类型的上边界(后面还会讲到使用super关键字指定泛型的下边界),即泛型只能适用于extends关键字后面类或接口的子类。

Java泛型编程的边界可以是多个,使用如<T extends A & B & C>语法来声明,其中只能有一个是类,并且只能是extends后面的第一个为类,其他的均只能为接口(和类/接口中的extends意义不同)。

使用了泛型边界之后,泛型对象就可以使用边界对象中公共的成员变量和方法。

2.泛型通配符:

泛型初始化过程中,一旦给定了参数类型之后,参数类型就会被限制,无法随着复制的类型而动态改变,如:

[java]  view plain copy
  1. class Fruit{  
  2. }  
  3. class Apple extends Fruit{  
  4. }  
  5. class Jonathan extends Apple{  
  6. }  
  7. class Orange extends Fruit{  
  8. }  
  9. 如果使用数组:  
  10. public class ConvariantArrays{  
  11.     Fruit fruit = new Apple[10];  
  12.     Fruit[0] = new Apple();  
  13.     Fruit[1] = new Jonathan();  
  14.     try{  
  15.         fruit[0] = new Fruit();  
  16. }catch(Exception e){  
  17.     System.out.println(e);  
  18. }  
  19. try{  
  20.         fruit[0] = new Orange();  
  21. }catch(Exception e){  
  22.     System.out.println(e);  
  23. }  
  24. }  

编译时没有任何错误,运行时会报如下异常:

java.lang.ArrayStoreException:Fruit

java.lang.ArrayStoreException:Orange

为了使得泛型在编译时就可以进行参数类型检查,我们推荐使用java的集合容器类,如下:

[java]  view plain copy
  1. public class NonConvariantGenerics{  
  2.     List<Fruit> flist = new ArrayList<Apple>();  
  3. }  

很不幸的是,这段代码会报编译错误:incompatible types,不兼容的参数类型,集合认为虽然Apple继承自Fruit,但是List的Fruit和List的Apple是不相同的,因为泛型参数在声明时给定之后就被限制了,无法随着具体的初始化实例而动态改变,为解决这个问题,泛型引入了通配符”?”。

对于这个问题的解决,使用通配符如下:

[java]  view plain copy
  1. public class NonConvariantGenerics{  
  2.     List<? extends Fruit> flist = new ArrayList<Apple>();  
  3. }  

泛型通配符”?”的意思是任何特定继承Fruit的类,java编译器在编译时会根据具体的类型实例化。

另外,一个比较经典泛型通配符的例子如下:

public class SampleClass < T extendsS> {…}

假如A,B,C,…Z这26个class都实现了S接口。我们使用时需要使用到这26个class类型的泛型参数。那实例化的时候怎么办呢?依次写下

SampleClass<A> a = new SampleClass();

SampleClass<B> a = new SampleClass();

SampleClass<Z> a = new SampleClass();

这显然很冗余,还不如使用Object而不使用泛型,使用通配符非常方便:

SampleClass<? Extends S> sc = newSampleClass();

3.泛型下边界:

在1中大概了解了泛型上边界,使用extends关键字指定泛型实例化参数只能是指定类的子类,在泛型中还可以指定参数的下边界,是一super关键字可以指定泛型实例化时的参数只能是指定类的父类。

例如:

[java]  view plain copy
  1. class Fruit{  
  2. }  
  3. class Apple extends Fruit{  
  4. }  
  5. class Jonathan extends Apple{  
  6. }  
  7. class Orange extends Fruit{  
  8. }  
  9. public superTypeWildcards{  
  10.     public static void writeTo(List<? super Apple> apples){  
  11.         apples.add(new Apple());  
  12.         apples.add(new Jonathan());  
  13. }  
  14. }  

通过? Super限制了List元素只能是Apple的父类。

泛型下边界还可以使用<?super T>,但是注意不能使用<Tsuper A>,即super之前的只能是泛型通配符,如:

[java]  view plain copy
  1. public class GenericWriting{  
  2.     static List<Apple> apples = new ArrayList<Apple>();  
  3.     static List<Fruit> fruits = new ArrayList<Fruit>();  
  4.     static <T> void writeExact(List<T> list, T item){  
  5.         list.add(item);  
  6. }  
  7. static <T> void writeWithWildcards(List<? super T> list, T item){  
  8.     list.add(item);  
  9. }  
  10. static void f1(){  
  11.     writeExact(apples, new Apple());  
  12. }  
  13. static void f2(){  
  14. writeWithWildcards(apples, new Apple());  
  15.     writeWithWildcards(fruits, new Apple());  
  16. }  
  17. public static void main(String[] args){  
  18.     f1();  
  19.     f2();  
  20. }  
  21. }  

4.无边界的通配符:

泛型的通配符也可以不指定边界,没有边界的通配符意思是不确定参数的类型,编译时泛型檫除类型信息,认为是Object类型。如:

[java]  view plain copy
  1. public class UnboundedWildcard{  
  2.     static List list1;  
  3.     static List<?> list2;  
  4.     static List<? extends Object> list3;  
  5.     static void assign1(List list){  
  6.         list1 = list;  
  7.         list2 = list;  
  8.         //list3 = list; //有未检查转换警告  
  9. }   
  10. static void assign2(List<?> list){  
  11.         list1 = list;  
  12.         list2 = list;  
  13.     list3 = list;  
  14. }  
  15. static void assign3(List<? extends Object> list){  
  16.         list1 = list;  
  17.         list2 = list;  
  18.     list3 = list;  
  19. }  
  20. public static void main(String[] args){  
  21.     assign1(new ArrayList());  
  22. assign2(new ArrayList());  
  23. //assign3(new ArrayList()); //有未检查转换警告  
  24. assign1(new ArrayList<String>());  
  25. assign2(new ArrayList<String>());  
  26. assign3(new ArrayList<String>());   
  27. List<?> wildList = new ArrayList();  
  28. assign1(wildList);  
  29. assign2(wildList);  
  30. assign3(wildList);   
  31. }  
  32. }  

List和List<?>的区别是:List是一个原始类型的List,它可以存放任何Object类型的对象,不需要编译时类型检查。List<?>等价于List<Object>,它不是一个原始类型的List,它存放一些特定类型,只是暂时还不确定是什么类型,需要编译时类型检查。因此List的效率要比List<?>高。

5.实现泛型接口注意事项:

由于泛型在编译过程中檫除了参数类型信息,所以一个类不能实现以泛型参数区别的多个接口,如:

[java]  view plain copy
  1. interface Payable<T>{  
  2. }  
  3. class Employee implements Payable<Employee>{  
  4. }  
  5. class Hourly extends Employee implements Payable<Hourly>{  
  6. }  

类Hourly无法编译,因为由于泛型类型檫除,Payable<Employee>和Payable<Hourly>在编译时是同一个类型Payable,因此无法同时实现一个接口两次。

6.泛型方法重载注意事项:

由于泛型在编译时将参数类型檫除,因此以参数类型来进行方法重载在泛型中要特别注意,如:

[java]  view plain copy
  1. public class GenericMethod<W,T>{  
  2.     void f(List<T> v) {  
  3. }  
  4. void f(List<W> v){  
  5. }  
  6. }  

无法通过编译,因为泛型檫除类型信息,上面两个方法的参数都被看作为Object类型,使用参数类型已经无法区别上面两个方法,因此无法重载。

7.泛型中的自绑定:

通常情况下,一个类无法直接继承一个泛型参数,但是你可以通过继承一个声明泛型参数的类,这就是java泛型编程中的自绑定,如:

[java]  view plain copy
  1. class SelfBounded<T extends SelfBounded<T>>{  
  2.     T element;  
  3.     SelfBounded<T> set(T arg){  
  4.         Element = arg;  
  5.         return this;  
  6. }   
  7. T get(){  
  8.     return element;  
  9. }  
  10. }  
  11. class A extends SelfBounded<A>{  
  12. }  
  13. class B extends SelfBounded<A>{  
  14. }  
  15. class C extends SelfBounded<C>{  
  16.     C setAndGet(C arg){  
  17.         set(arg);  
  18.         return get();  
  19. }  
  20. }  
  21. public class SelfBounding{  
  22.     public static void main(String[] args){  
  23.         A a = new A();  
  24.         a.set(new A());  
  25.         a = a.set(new A()).get();  
  26.         a = a.get();  
  27.         C c = new C();  
  28.         C = c.setAndGet(new C());  
  29. }  
  30. }  

泛型的自绑定约束目的是用于强制继承关系,即使用泛型参数的类的基类是相同的,强制所有人使用相同的方式使用参数基类。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值