《Java编程思想》学习笔记8——泛型编程高级
来源:互联网 发布:杜兰特奥运会数据 编辑:程序博客网 时间:2024/05/21 14:08
1.泛型边界:
Java泛型编程时,编译器忽略泛型参数的具体类型,认为使用泛型的类、方法对Object都适用,这在泛型编程中称为类型信息檫除。
例如:
class GenericType{public static void main(String[] args){System.out.println(new ArrayList<String>().getClass());System.out.println(new ArrayList<Integer>().getClass());}}
输出结果为:
java.util.ArrayList
java.util.ArrayList
泛型忽略了集合容器中具体的类型,这就是类型檫除。
但是如果某些泛型的类/方法只想针对某种特定类型获取相关子类应用,这时就必须使用泛型边界来为泛型参数指定限制条件。
例如:
interface HasColor{java.awt.Color getColor();}class Colored<T extends HasColor>{T item;Colored(T item){this.item = item;}java.awt.Color color(){//调用HasColor接口实现类的getColor()方法return item.getColor();}}class Dimension{public int x, y, z;}Class ColoredDimension<T extends Dimension & HasColor>{T item;ColoredDimension(T item){this.item = item;}T getItem(){return item;}java.awt.Color color(){//调用HasColor实现类中的getColor()方法return item.getColor();}//获取Dimension类中定义的x,y,z成员变量int getX(){return item.x;}int getY(){return item.y;}int getZ(){return item.z;}}interface Weight{int weight();}class Solid<T extends Dimension & HasColor & Weight>{T item;Solide(T item){this.item = item;}T getItem(){return item;}java.awt.Color color(){//调用HasColor实现类中的getColor()方法return item.getColor();}//获取Dimension类中定义的x,y,z成员变量int getX(){return item.x;}int getY(){return item.y;}int getZ(){return item.z;}int weight(){//调用Weight接口实现类的weight()方法return item.weight();}}class Bounded extends Dimension implements HasColor, Weight{public java.awt.Color getColor{return null;}public int weight(){return 0;}}public class BasicBounds{public static void main(String[] args){Solid<Bounded> solid = new Solid<Bounded>(new Bounded());solid.color();solid.getX();solid.getY();solid.getZ();solid.weight();}}
Java泛型编程中使用extends关键字指定泛型参数类型的上边界(后面还会讲到使用super关键字指定泛型的下边界),即泛型只能适用于extends关键字后面类或接口的子类。
Java泛型编程的边界可以是多个,使用如<T extends A & B & C>语法来声明,其中只能有一个是类,并且只能是extends后面的第一个为类,其他的均只能为接口(和类/接口中的extends意义不同)。
使用了泛型边界之后,泛型对象就可以使用边界对象中公共的成员变量和方法。
2.泛型通配符:
泛型初始化过程中,一旦给定了参数类型之后,参数类型就会被限制,无法随着复制的类型而动态改变,如:
class Fruit{}class Apple extends Fruit{}class Jonathan extends Apple{}class Orange extends Fruit{}如果使用数组:public class ConvariantArrays{Fruit fruit = new Apple[10];Fruit[0] = new Apple();Fruit[1] = new Jonathan();try{fruit[0] = new Fruit();}catch(Exception e){System.out.println(e);}try{fruit[0] = new Orange();}catch(Exception e){System.out.println(e);}}
编译时没有任何错误,运行时会报如下异常:
java.lang.ArrayStoreException:Fruit
java.lang.ArrayStoreException:Orange
为了使得泛型在编译时就可以进行参数类型检查,我们推荐使用java的集合容器类,如下:
public class NonConvariantGenerics{List<Fruit> flist = new ArrayList<Apple>();}
很不幸的是,这段代码会报编译错误:incompatible types,不兼容的参数类型,集合认为虽然Apple继承自Fruit,但是List的Fruit和List的Apple是不相同的,因为泛型参数在声明时给定之后就被限制了,无法随着具体的初始化实例而动态改变,为解决这个问题,泛型引入了通配符”?”。
对于这个问题的解决,使用通配符如下:
public class NonConvariantGenerics{List<? extends Fruit> flist = new ArrayList<Apple>();}
泛型通配符”?”的意思是任何特定继承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关键字可以指定泛型实例化时的参数只能是指定类的父类。
例如:
class Fruit{}class Apple extends Fruit{}class Jonathan extends Apple{}class Orange extends Fruit{}public superTypeWildcards{public static void writeTo(List<? super Apple> apples){apples.add(new Apple());apples.add(new Jonathan());}}
通过? Super限制了List元素只能是Apple的父类。
泛型下边界还可以使用<?super T>,但是注意不能使用<Tsuper A>,即super之前的只能是泛型通配符,如:
public class GenericWriting{static List<Apple> apples = new ArrayList<Apple>();static List<Fruit> fruits = new ArrayList<Fruit>();static <T> void writeExact(List<T> list, T item){list.add(item);}static <T> void writeWithWildcards(List<? super T> list, T item){list.add(item);}static void f1(){writeExact(apples, new Apple());}static void f2(){writeWithWildcards(apples, new Apple());writeWithWildcards(fruits, new Apple());}public static void main(String[] args){f1();f2();}}
4.无边界的通配符:
泛型的通配符也可以不指定边界,没有边界的通配符意思是不确定参数的类型,编译时泛型檫除类型信息,认为是Object类型。如:
public class UnboundedWildcard{static List list1;static List<?> list2;static List<? extends Object> list3;static void assign1(List list){list1 = list;list2 = list;//list3 = list; //有未检查转换警告} static void assign2(List<?> list){list1 = list;list2 = list;list3 = list;}static void assign3(List<? extends Object> list){list1 = list;list2 = list;list3 = list;}public static void main(String[] args){assign1(new ArrayList());assign2(new ArrayList());//assign3(new ArrayList()); //有未检查转换警告assign1(new ArrayList<String>());assign2(new ArrayList<String>());assign3(new ArrayList<String>()); List<?> wildList = new ArrayList();assign1(wildList);assign2(wildList);assign3(wildList); }}
List和List<?>的区别是:List是一个原始类型的List,它可以存放任何Object类型的对象,不需要编译时类型检查。List<?>等价于List<Object>,它不是一个原始类型的List,它存放一些特定类型,只是暂时还不确定是什么类型,需要编译时类型检查。因此List的效率要比List<?>高。
5.实现泛型接口注意事项:
由于泛型在编译过程中檫除了参数类型信息,所以一个类不能实现以泛型参数区别的多个接口,如:
interface Payable<T>{}class Employee implements Payable<Employee>{}class Hourly extends Employee implements Payable<Hourly>{}
类Hourly无法编译,因为由于泛型类型檫除,Payable<Employee>和Payable<Hourly>在编译时是同一个类型Payable,因此无法同时实现一个接口两次。
6.泛型方法重载注意事项:
由于泛型在编译时将参数类型檫除,因此以参数类型来进行方法重载在泛型中要特别注意,如:
public class GenericMethod<W,T>{void f(List<T> v) {}void f(List<W> v){}}
无法通过编译,因为泛型檫除类型信息,上面两个方法的参数都被看作为Object类型,使用参数类型已经无法区别上面两个方法,因此无法重载。
7.泛型中的自绑定:
通常情况下,一个类无法直接继承一个泛型参数,但是你可以通过继承一个声明泛型参数的类,这就是java泛型编程中的自绑定,如:
class SelfBounded<T extends SelfBounded<T>>{T element;SelfBounded<T> set(T arg){Element = arg;return this;} T get(){return element;}}class A extends SelfBounded<A>{}class B extends SelfBounded<A>{}class C extends SelfBounded<C>{C setAndGet(C arg){set(arg);return get();}}public class SelfBounding{public static void main(String[] args){A a = new A();a.set(new A());a = a.set(new A()).get();a = a.get();C c = new C();C = c.setAndGet(new C());}}
泛型的自绑定约束目的是用于强制继承关系,即使用泛型参数的类的基类是相同的,强制所有人使用相同的方式使用参数基类。
- 《Java编程思想》学习笔记8——泛型编程高级
- 《Java编程思想》学习笔记8——泛型编程高级
- 《Java编程思想》学习笔记8——泛型编程高级
- 《Java编程思想》学习笔记9——集合容器高级
- 《Java编程思想》学习笔记14——I/O高级
- 《Java编程思想》学习笔记9——集合容器高级
- 《Java编程思想》学习笔记9——集合容器高级
- 《Java编程思想》学习笔记14——I/O高级
- 《Java编程思想》学习笔记9——集合容器高级
- 《Java编程思想》学习笔记14——I/O高级
- 《Java编程思想》学习笔记7——泛型编程基础
- 《Java编程思想》学习笔记7——泛型编程基础 .
- 《Java编程思想》学习笔记7——泛型编程基础
- 《Java编程思想》学习笔记7——泛型编程基础
- JAVA编程思想学习笔记
- JAVA编程思想 学习笔记
- java编程思想学习笔记
- Java编程思想学习笔记
- core dump
- SCCM with SP2安装出现创建和初始化站点数据库失败
- C++基础知识学习笔记(二)
- java实现的 猜拳游戏
- Jquery validator 处理ajax表单提交的验证
- 《Java编程思想》学习笔记8——泛型编程高级
- 初学J2ME的时,看过的好文章(一)
- android edittext 键盘弹出屏幕自适应
- kdict
- 矩阵连乘的最少次数——dp
- 初学J2ME时,看过的好文章(二)
- listview 中的滚动条一直显示
- NSIS Unicode
- 项目去除SVN