《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());}}

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


原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 ddrops d3滴多了怎么办 维生素d滴多了怎么办 ddrops最后滴不出来的怎么办 ddrops一次滴3滴怎么办 小孩子头睡偏了怎么办 7岁儿童头睡偏了怎么办 婴儿后脑睡平了怎么办 六个月宝宝免疫力低怎么办 十个月宝宝食烧怎么办 6个月宝宝感冒了怎么办 5个月宝宝腿短怎么办 5个月宝宝太瘦怎么办 宝宝喝了浓奶粉怎么办 一岁把尿不尿怎么办 五个月的宝宝大便干燥怎么办 婴儿便秘怎么办什么方法最有效 8个月小孩便秘怎么办 1个月新生儿便秘怎么办 新生儿头竖立0分怎么办 20天的宝宝便秘怎么办 出生23天的宝宝便秘怎么办 喝奶粉的宝宝便秘怎么办 抱孩子抱的驼背怎么办 4岁宝宝不拉屎怎么办 小朋友大便拉不出来怎么办 儿童便秘拉不出来怎么办 没感冒喉咙有痰怎么办 宝宝喂不进去药怎么办 新生儿只放屁不拉大便怎么办 新生儿腹胀不拉大便怎么办 8月宝宝咳嗽有痰怎么办 2个月婴儿惊吓怎么办 吃了米粉不拉屎怎么办 奇异果奶昔苦了怎么办 8个月宝宝偏瘦怎么办 一岁宝宝螺旋腿怎么办 七个月宝宝晚上咳嗽厉害怎么办 孕7个月感冒咳嗽怎么办 4个月婴儿肺炎怎么办 宝宝吃胡萝卜泥拉肚子怎么办 宝宝吃土豆泥不消化怎么办