深入学习Java-泛型

上传人:一*** 文档编号:243406979 上传时间:2024-09-22 格式:PPT 页数:67 大小:1.97MB
返回 下载 相关 举报
深入学习Java-泛型_第1页
第1页 / 共67页
深入学习Java-泛型_第2页
第2页 / 共67页
深入学习Java-泛型_第3页
第3页 / 共67页
点击查看更多>>
资源描述
,单击此处编辑母版标题样式,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,#,泛型,泛型入门,深入泛型,类型通配符,泛,型方法,擦除与转换,泛,型与数组,本章小结,本章要点:,本章的知识是,Java,集合的补充阅读,因为,JDK1.5,增加泛型支持在很大程度上都是为了让集合能记住其元素的数据类型。在没有泛型之前,一旦把一个对象“丢进”,Java,集合中,集合就会忘记对象的类型,把所有的对象当做,Object,类型处理。当程序从集合中取走对象后,就需要进行强制类型转换,这种强制类型转换不仅是代码臃肿,而且容易引起,ClassCastException,一场。,增加泛型支持后的集合,完全可以记住集合中的元素的类型,并可以在编译时检查集合中元素的类型,如果试图向集合中添加不满足类型要求的对象,编译器就会提示错误。增加泛型后的集合,可以让代码更加简洁,程序更加健壮(,Java,泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生,ClassCastException,异常)。,Java,集合有一个缺点,-,把一个对象“丢进”集合里之后,集合就会忘记这个对象的数据类型,当再次取出该对象时,该对象的编译类型就变成了,Object,类型。(其运行时类型没变)。,Java,集合之所以被设计成这样,是因为集合的设计者不知道我们会用集合来保存什么类型的对象,所以他们把集合设计成能保存任何类型的对象,只要求具有很好的通用性。但这样做带来如下两个问题:,集合对元素类型没有任何限制,这样可以引发一些问题。,由于把对象“丢进”集合时,集合丢失了对象的状态信息,集合只知道它盛装的是,Object,,因此取出集合元素后通常还需要进行强制类型转换。这种强制类型转换即增加了编程的复杂度,也可能引发,ClassCastException,异常。,第一节 泛型入门,第一节 泛型入门,第一节 泛型入门,第一节 泛型入门,没有泛型的时候,我们需要一个箱子,可以放入一个对象,也可以取出对象。,public class Box ,private Object object;,public Object getObject() ,return object;,public void setObject(Object object) ,this.object = object;,在使用中,每次取出的东西都必须强制类型转换,不但麻烦,而且容易出现类型转换异常。,Box box1=new Box();,box1.setObject(hello,);,String s = (String)box1.getObject();,Box box2=new Box();,box2.setObject(Integer.valueOf(123);,/,中间经过一些处理,/,取出时必须强制类型转换,Integer itg=(Integer)box2.getObject();,/,编译器无法判断这是个错误的类型转换,String errorStr=(String)box2.getObject();,Exception in thread main java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String,at Box.main(Box.java:22),Java5.0,中引入了泛型,在实例化箱子的时候可以“贴上标签”,规定箱子必须放入对象的类型、取出的对象类型,这样就无需强制类型转换了。,Box box1=new Box();,box1.setObject(hello);,Box box2=new Box();,box2.setObject(Integer.valueOf(123);,/,中间经过一些处理,/,取出时必须强制类型转换,Integer itg=(Integer)box2.getObject();,/,编译器无法判断这是个错误的类型转换,String errorStr=(String)box2.getObject();,public class Box,private T t;,public T getObject(),return t;,public void setObject(T t),this.t=t;,1.,编译时不检查类型的异常,在输出,5,的长度时,程序试图将,Integer,对象转换为,String,类型引发,ClassCastException,异常。,从,Java5,之后,,Java,引入了“参数化类型”的概念,允许程序在创建集合时指定集合元素的类型。,Java,的参数化类型被称为泛型(,Generic,)。,2.,使用泛型,上面程序成功创建了一个特殊的,List,集合:,strList,,这个,List,集合只能保存字符串对象,不能保存其他类型的对象。创建这种特殊集合的方法是:在集合接口、类后增加尖括号,尖括号里放一个数据类型,即表明这个集合接口、集合类只能保存特定类型的对象。注意,List,后面的类型声明,它指定,strList,不是一个任意的,List,,而是一个,String,类型的,list,,写作:,List,。可以称,List,是带一个类型参数的泛型接口,在本例中,类型参数是,String,。在创建这个,ArrayList,对象时也指定了一个类型参数。,程序在取出集合元素时不需要进行强制类型转换,因为该对象已经记住了它是什么类型的集合。,在,Java 7,以前,如果使用带泛型的接口、类定义变量,那么调用构造器创建对象时构造器的后面也必须带泛型,这显得有些多余了。例如:,上面两条语句中后面尖括号的类型声明完全多余,在,Java7,之前是必须的,不能省略。从,Java7,开始,,Java,允许在构造器后不要带完整的泛型信息,只要给出一对尖括号即可,,Java,可以推断尖括号里应该是什么泛型信息。即上面可以改写为:,3. Java 7,泛型的“菱形”语法,实例,使用菱形语法,对泛型并不会改变,只是更好的简化了泛型编程。,所谓泛型,就是允许在定义类、接口、方法时使用类型形参,这个类型形参将在声明变量、创建对象、调用方法时动态地指定(即传入实际的类型参数,也可称为类型实参)。,Java 5,改写了集合框架中的全部接口和类,为这些接口、类增加了泛型支持,从而可以在声明集合变量、创建集合对象时传入类型实参,这就是在前面程序中看到的,List,和,ArrayList,两种类型。,第二,节 深入泛型,泛型,的实质:允许在定义接口、类时声明类型形参,类型形参在整个接口、类体内可当成类型使用,几乎所有可使用普通类型的地方都可以使用这种类型形参,。,虽然程序只定义了一个,List,接口,但实际上使用时可以产生无数多个,List,接口,只要为,E,传入不同的类型实参,系统就会多出一个新的,List,子接口。必须指出:,LIst,绝对不会被替换成,ListString,,系统没有尽心源代码赋值,二进制代码中没有,磁盘中没有,内存中也没有。,包含泛型声明的类型可以在定义变量、创建对象时传入一个类型实参,从而可以动态地生成无数多个逻辑上的子类,但这种子类在物理上并不存在。,1.,定义泛型接口、类,可以为任何类、接口增加泛型声明(并不是只有集合类才可以使用泛型声明,虽然集合类是泛型的重要使用场所)。下面自定义一个类,此类包含泛型声明。,实例,上面程序定义了一个带泛型声明的,Apple,类(不要理会这个类型形参是否有实际意义),使用,Apple,类时就可以为,T,类型形参传入实际类型,这样就可以生成如,Apple,、,Apple.,形式的多个逻辑子类(物理上并不存在)。这就是第一节中,List,、,ArrayList,等类型的原因,-JDK,在定义,List,、,ArrayList,等接口、类时使用了类型形参,所以在使用这些类时为之传入了实际的类型参数。,注意:当创建带泛型声明的自定义类,为该类定义构造器时,构造器名还是原来的类名,不要增加泛型声明。例如,为,Apple,类定义构造器,其构造器名依然是,Apple,,而不是,Apple,!调用该构造器时却可以使用,Apple,的形式,当然应该为,T,形参传入实际的类型参数。,Java7,提供了菱形语法,允许省略,中的类型实参。,当创建了带泛型声明的接口、父类之后,可以为该接口创建实现类,或者从该父类派生子类,需要指出的是,当使用这些接口、父类时不能再包含类型形参。例如,下面代码是错误的。,方法中的形参代表变量、常量、表达式等数据,本书把它们直接成为形参,或者称为数据形参。定义方法时可以声明数据形参,调用方法(使用方法)时必须为这些数据形参传入实际的数据;,如果想从,Apple,类派生一个子类,则可以改为如下代码:,2.,从泛型类派生子类,调用方法时必须为所有的数据形参传入参数值,与调用方法不同的是,使用类、接口时也可以不为类型形参参入实际的类型参数。,如果从,Apple,类派生子类,则在,Apple,类中所有使用,T,类型形参的地方都将被替换成,String,类型,即它的子类将会继承到,String getInfo(),和,void setInfo(String info),两个方法,如果子类需要重写父类的方法,就必须注意这一点。,实例,如果使用了,Apple,类是没有传入实际的类型参数,,Java,编译器可能发出警告:使用了未经检查或不安全的操作,-,这就是泛型检查的警告。如果希望看到该警告的更详细信息,则可以通过为,Javac,命令增加,-Xlink,:,unchecked,选项类实现。此时,系统会把,Apple,类里的,T,形参当成,Object,类型处理。,上面程序都是从带泛型声明的父类来派生子类,创建带泛型声明的接口的实现类与此几乎完全一样,此处不要赘述。,前面提到可以把,ArrayList,类当成,ArrayList,的子类,事实上,,ArrayList,类也确实像一种特殊的,ArrayList,类:该,ArrayList,对象只能添加,String,对象作为集合元素。旦实际上,系统并没有为,ArrayList,生成,新,的,Class,文件,而且也不会把,ArrayList,当成新类来处理。,3.,并不存在泛型类,看下面代码打印结果:,因为不管泛型的实际参数是什么,它们在运行时总有同样的类(,class,)。,不管为泛型的类型形参传入哪一种类型实参,对于,Java,来说,它们依然被当成同一个类处理,在内存中也只占同一块内存空间,,因此在静态方法、静态初始化或者静态变量的声明和初始化中不允许使用类型形参,。,实例,由于系统中并不会真正生成泛型类,所以,instanceof,运算符后不能使用泛型类。例如,下面的代码是错误的。,正如前面所讲的,当使用一个泛型类时(包括声明变量和创建对象两种情况),都应该为这个泛型类传入一个类型实参。如果没有传入类型实际参数,编译器就会提出泛型警告。假设现在需要定义一个方法,该方法里有一个集合形参,集合形参的元素类型是不确定的,那应该怎样定义呢?,考虑以下代码:,上面程序当然没有问题:这是一段最普通的遍历,List,集合的代码。问题是上面程序中,List,是一个有泛型声明的接口,此处使用,List,接口时没有传入实际类型参数,这强引起泛型警告。,第三节 类型通配符,为此,考虑为,List,接口传入实际的类型参数,-,因为,List,集合里的元素类型是不确定的,将上面方法改为如下形式:,表面上看来,上面方法声明没有问题,这个方法声明确实没有任何问题。问题是调用该方法传入的实际参数值是可能不是我们所期望的,当试图调用该方法是,可能会出现编译错误。,如果,Foo,是,Bar,的一个子类型(子类或者子接口),而,G,是具有泛型声明的类或接口,,G,并不是,G,的子类型!这一点非常值得注意,因为它与大部分人的习惯认为是不同的。,注意,与数组进行比较,先看一下数组是如何工作的。在数组中,程序可以直接把一个,Integer,数组赋给一个,Number,变量。如果试图把一个,Double,对象保存到,Number,数组中,编译可以通过,但在运行时抛出,ArrayStoreException,异常。,这是,一种潜在的风险。,一门设计优秀的语言,不仅需要提供强大的功能,而且能够提供强大的“错误提示”和“错误警告”,这样才能尽量避免开发者犯错。而,Java,允许,Integer,数组赋值给,Number,显然不是一种安全的设计。,在,Java,的早期设计中,允许,Integer,数组赋值给,Number,变量存在缺陷,因此,Java,在泛型设计时进行了改进,它不在允许把,List,对象赋值给,List,变量。例如,如下代码将会编译出错。,Java,泛型的设计原则是,只要代码在编译时没有出现警告,就不会遇到运行时,ClassCastException,异常。,数组和泛型有所不同,假设,Foo,是,Bar,的一个子类型(子类或者子接口),那么,Foo,依然是,Bar,的子类型;但,G,不是,G,的子类型。,注意,为了表示各种泛型,List,的子类,可以使用类型通配符,类型通配符是一个问号(?),将一个问号作为类型实参传给,List,集合,写作:,List,(意思是元素类型未知的,List,)。这个问号(?)被称为通配符,它的元素类型可以匹配任何类型。可以将上面方法改写为如下形式:,现在使用任何类型的,List,来调用它,程序依然可以访问集合,c,中的元素,其类型是,Object,,这永远是不安全的,因为不管,List,的真实类型是什么,它包含的都是,Object,。,上面程序中使用的,List,其实这种写法可以适应于任何支持泛型声明的接口和类,比如写成,Set,、,Collection,、,Map,等。,1.,使用类型通配符,但这种带通配符的,List,仅表示它是各种泛型的,List,的父类,并不能把元素加入到其中。例如,如下代码会引起编译错误。,因为程序无法确定,c,集合中元素的类型,所以不能像其中添加对象。根据前面的,List,接口定义的代码可以发现:,add(),方法有类型参数,E,作为集合的元素类型,所以传给,add,的参数必须是,E,类的对象或者其子类的对象。但因为在该例中不知道,E,是什么类型,所以程序无法将任何对象“丢进”该集合。唯一的例外是,null,,它是所有引用类型的实例。,另一方面,程序可以调用,get(),方法来返回,List,集合指定所引处的元素,其返回值是一个位置类型,但可以肯定的是,它总是一个,Object,。因此,把,get(),的返回值赋值给一个,Object,类型的变量,或者放在任何希望是,Object,类型的地方都可以。,当直接使用,List,这种形式时,即表明这个,List,集合可以是任何泛型,List,的父类。但还有一种特殊的情形,程序不希望这个,List,是任何泛型,List,的父类,只希望它代表某一类泛型,List,的父类。考虑一个简单的绘图程序,下面定义三个形状类。,2.,设定类型通配符的上限,List,并不是,List,的子类型,所以不能把,List,对象当成,List,使用。为了表示,List,的父类,可以考虑使用,List,把,Canvas,改为如下形式,上面程序使用了通配符来表示所有的类型。上面的,drawAll(),方法可以接受,List,对象作为参数,问题是上满的方法实现体显得极为臃肿而繁琐:使用了泛型还需要强制类型转换。,实际上需要一种泛型表示方法,它可以表示所以,Shape,泛型,List,的父类。为了满足这种需求,,Java,泛型提供了被限制的泛型通配符。被限制的泛型通配符表示如下。,有了这种被限制的泛型通配符,就可以把上面的,Canvas,程序改为如下形式:,将,Canvas,改为如上形式,就可以把,List,对象当成,List,使用。即,List,可以表示,List,、,List,的父类,-,只要,List,后尖括号的类型是,Shape,的子类型即可。,List,是受限制通配符的例子,此处的问号代表一个位置的类型,就像前面看到的通配符一样。但是在此处的这个未知类型一定是,Shape,的子类型(也可以是,Shape,本身),因此可以把,Shape,称为这个通配符的上限(,upper bound,)。,类似的,由于程序无法确定这个受限的通配符的具体类型,所以不能把,Shape,对象或其子类的对象加入这个泛型集合中。,与普通通配符相似的是,,shape.add(),的第二个参数类型是?,extends Shape,它表示,Shape,位置的子类,程序无法确定这个类型是什么,所以无法将任何对象添加到这种集合中。,Java,泛型不仅允许在使用通配符形参时设定上限,而且可以在定义类型形参时设定上限。用于表示传给该类型形参的实际类型要么是该上限类型,要么是该上限类型的子类。,上面程序定义了一个,Apple,泛型类,该,Apple,类的类形参的上限是,Number,类,这表明使用,Apple,类时为,T,形参传入的实际类型参数只能是,Number,或,Number,类的子类。,在一种更极端的情况下,程序需要为类型形参设定多个上限(至少有一个父类上限,可以有多个接口上限),表明该类型形参必须是其父类的子类(是父类本身也行),并且实现多个上限接口。如下:,3.,设定类型形参的上限,与类同时继承父类,实现接口类似的是,为类型形参指定多个上限时,所有的接口上限必须位于类上限之后。也就是说,如果需要为类型形参指定类上限,类上限必须位于第一位。,前面介绍了在定义类、接口时可以使用类型形参,在该类的方法定义和成员变量定义、接口的方法定义中,这些类型形参可被当成普通类型来用。在另一些情况下,定义类、接口时没有使用类型形参,但定义方法时向自己定义类型形参,这也是可以的,,Java 5,还提供了对泛型方法的支持。,第四节 泛型方法,假设需要实现这样一个方法,-,该方法负责将一个,Object,数组的所有元素添加到一个,Collection,集合中。考虑如下代码来实现该方法。,上面定义的方法没有任何问题,关键在于方法中的,c,形参,它的数据类型是,Collection,。正如前面介绍的,,Collection,不是,Collection,的子类型,-,所以这个方法的功能非常有限,它只能将,Object,数组的元素复制到元素位,Object,(,Ojbect,的子类不行)的,Collection,集合中,即下面的代码将会引起编译错误。,可见上面方法的参数类型不可以使用,Collection,,那使用通配符,Collection,是否可行呢?显然不可行,因为,Java,不允许把对象放进一个位置类型的集合中。,1.,定义泛型方法,为了解决这个问题,可以使用,Java 5,提供的泛型方法(,Generic method,)。所谓泛型方法,就是在声明方法时定义一个或多个类型形参。泛型方法的语法格式如下:,把上面方法的格式和普通方法的格式进行对比,不难发现泛型方法的方法签名比普通方法的方法签名多了类型形参声明,类型形参声明以尖括号括起来,多个类型形参之间以逗号隔开,所有的类型形参声明放在方法修饰符合方法返回值类型之间。采用支持泛型的方法,就可以把上面的,fromArrayToCollection,方法改为如下形式:,实例,上面程序定义了一个泛型方法,该泛型方法中定义了一个,T,类型形参,这个,T,类型形参就可以在该方法内当成普通类型使用。与接口、类声明中定义的类型形参不同的是,方法声明中定义的形参只能在该方法里使用,而接口、类声明中定义的类型形参则可以在整个接口、类中使用。,与类、接口中使用泛型参数不同的是,方法中的泛型参数无需显式传入实际类型参数,如上面程序所示,当程序调用,fromArrayToCollection(),方法时,无须在该方法前传入,String,、,Object,等类型,当系统仍然可以知道类型形参的数据类型,因为编译器根据实参推断类型形参的值,它通常推断出最直接的类型参数。,实例,上面程序中定义了,test(),方法,该方法用于将前一个集合里的元素复制到下一个集合中,该方法中的两个形参,from,to,的类型都是,Collection,,这要求调用该方法时的两个实参中的泛型类型相同,否则编译器无法准确地推断出泛型方法中类型形参的类型,上面程序中调用了,test,方法传入了两个实际参数,其中,as,的数据类型是,List,,而,ao,的数据类型是,List,,与泛型方法签名进行对比:,test(Collection a, Collection c),编译器无法正确识别,T,所代表的实际类型。为了避免这种错误,可以将该方法改为如下形式:,上面代码改变了,test(),方法签名,将该方法的前一个形参类型改为,Collection,,这种采用类型通配符的表示方式,只要,test(),方法的前一个,Collection,集合里的元素类型是后一个,Collection,集合里元素类型的子类即可。,那么,到底何时使用泛型方法?何时使用类型通配符?接下来详细介绍泛型方法和类型通配符的区别。,大多时候都可以使用泛型方法类代替类型通配符。例如,对于,Java,的,Collection,接口中两个方法定义:,上面集合中两个方法的形参都采用了类型通配符的形式,也可以采用泛型方法的形式,如下所示:,上面方法使用了,泛型形式,这时定义类型形参时设定上限(其中,E,是,Collection,接口里定义的类型形参,在该接口里,E,可当成普通类型使用)。,上面两个方法中类型形参,T,只,使用了一次,类型形参,T,产生的唯一效果是可以在不同的调用点传入不同的实际类型。对于这种情况,应该使用通配符:通配符就是被设计用来支持灵活的子类化的。,2.,泛型方法和类型通配符的区别,泛型方法允许类型形参被用来表示方法的一个或多个参数之间的类型依赖关系,或者方法返回值与参数之间的类型依赖关系。如果没有这样的类型依赖关系,就不应该使用泛型方法。,如果某个方法中一个形参(,a,)的类型或返回值的类型依赖于另一个形参(,b,)的类型,则形参(,b,)的类型声明不应该使用通配符,-,因为形参(,a,)或返回值的类型依赖于该形参(,b,)的类型,如果形参(,b,)的类型无法确定,程序就无法定义形参(,a,)的类型,在这种情况下,只能考虑使用在方法签名中声明类型形参,-,也就是泛型方法。,如果有需要,也可以同时使用泛型方法和通配符,如,Java,的,Collections.copy(),方法。,上面,copy,方法中的,dest,和,src,存在明显的依赖关系,从源,List,中复制出来的元素,必须可以“丢进”目标,List,中,所以源,List,集合元素的类型只能是目标集合元素的类型的子类型或者它本身,但,JDK,定义,src,形参类型是使用的是类型通配符,而不是泛型方法。这是因为,该方法无须向,src,集合中添加元,素,也无需修改,src,集合里的元素,所以可以使用类型通配符,无需使用泛型方法。,当然,也可以将上面的方法签名改为使用泛型方法,不使用类型通配符,如下所示:,这个方法签名可以代替前面的方法签名。但注意上面的类型形参,S,,它仅使用了一次,其他参数的类型、方法返回值的类型都不依赖于它,那类型形参,S,就没有存在的必要,既可以用通配符来代替,S,。使用通配符比使用泛型方法(在方法签名中显式声明类型形参)更加清晰和准确,因此,Java,设计该方法时采用了通配符,而不是泛型方法。,类型通配符与泛型方法(在方法签名中显式声明类型形参)还有一个显著的区别:类型通配符既可以在方法签名中定义形参的类型,也可以用于定义变量的类型;但泛型方法中的类型形参必须在对应方法中显示声明。,正如泛型方法允许在方法签名中声明类型形参一样,,Java,也允许在构造器签名中声明类型形参,这样就产生了所谓的泛型构造器。,一旦定义了泛型构造器,接下来在调用构造器时,就不仅可以让,Java,根据数据参数的类型来“推断”类型形参的类型,而且程序员也可以显式地为构造器中的类型形参指定实际的类型。,3. Java 7,的菱形语法与泛型构造器,前面介绍过,Java7,新增的“菱形”语法,它允许调用构造器时在构造器后使用一对尖括号来代表泛型信息。但如果程序显式指定了泛型构造器中声明的类型形参的实际类型,则不可以使用菱形语法。,上述代码中既指定了泛型构造器中的类型是,Integer,类型,又想使用菱形语法,所以这行代码无法通过编译。,假设自己实现一个工具方法:实现将,src,集合里的元素复制到,dest,集合里的功能,不管,src,集合元素的类型是什么,只要,dest,集合元素的类型与前者相同或是前者的父类即可。为了表达这种约束关系,,Java,允许设定通配符的下限:,,这个通配符表示它必须是,Type,本身,或是,Type,的父类。下面程序采用设定通配符下限的方式改写了前面的,copy(),方法:,4.,设定通配符下限,使用这种语句,就可以保证程序的,调用,后推断出最后一个被复制的元素类型是,Integer,,而不是笼统的,Number,类型。,因为泛型既允许设定通配符的上限,也允许设定通配符的下限,从而允许在一个类里包含如下两个方法定义:,5.,泛型方法与方法重载,Java8,改进了泛型方法的类型推断能力,类型推断主要有如下两个方面。,可通过调用方法的上下文来推断类型参数的目标类型。,可,在方法调用链中,将推断得到的类型参数传递到最后一个方法。,6. Java 8,改进的类型推断,需要指出的是,虽然,Java 8,增强了泛型推断的能力,但泛型推断不是万能的,例如下面的代码就是错误的:,在严格的泛型代码里,带泛型声明的类总应该带着类型参数。但为了与老的,Java,代码保持一致,也允许在使用带泛型声明的类时不指定实际的类型参数。如果没有为这个泛型类指定实际的类型参数,则该类型被称作,raw type,(原始数据),默认是声明该类型参数时指定的第一个上限类型。,当把一个具有泛型信息的对象赋给另一个没有泛型信息的变量时,所有在尖括号之间的类型信息都将被扔掉。比如一个,List,类型被转换为,List,,则该,List,对集合元素的类型检查变成了类型参数的的上限(即,Object,)。,第五节 擦除和转换,实例,从逻辑上来看,,List,是,List,的子类,如果直接把一个,List,对象赋给一个,List,对象应该引起编译错误,但实际上不会。对泛型而言,可以直接把一个,List,对象赋给一个,List,对象,编译器仅仅提示“未经检查的转换”,但访问里面的元素时会引起运行异常。,上面程序中定义了一个,List,对象,这个,List,对象保留了集合元素的类型信息。当把这个,List,对象赋给一个,List,类型的,list,后,编译器就会丢失前者的泛型信息,即丢失,list,集合里元素的类型信息,这是典型的擦除。,Java,又允许直接把,List,对象赋给一个,List,类型的变量,所以程序可以通过编译,只是发出未经检查的转换警告。但对,List,变量实际上引用的是,List,集合,所以当试图把该集合里的元素当成,String,类型的对象取出时,将引发运行时异常。,Java,泛型有一个很重要的设计原则,-,如果一段代码在编译时没有提出“,unchecked,未,见检查转换,”警告,则程序在运行时不会引发,ClassCastException,异常。正是基于这个原因,所以数组元素的类型不能包含类型变量或类型形参,除非是无上限的类型通配符。但可以声明元素类型包含类型变量或变量形参的数组。也就是说,只能声明,List,形式的数据,但不能创建,ArrayList10,这样的数组对象。假设,Java,支持穿件,ArrayList10,,则有如下程序。,第六节 泛型与数组,将上面程序改为如下程序:,上面程序声明了,List,类型的数组变量,这是允许的:但不允许创建,List,类型的对象,所以创建一个类型为,ArrayList10,的数组对象,这也是允许的。只是会有编译警告“,unchecked,未经检查的转换”,即编译器并不保证这段代码是安全的。,Java,允许创建无上限的通配符泛型数组,例如,new ArrayList10,,因此也可以将第一段代码改为使用无上限的通配符泛型数组,在这种情况下,程序不得不进行强制类型转换。,上面程序编译不会发出任何警告,但会引发,ClassCastException,异常。所以程序应该通过,instanceof,运算符来保证它的数据类型。,与此类似的是,创建元素类型是类型变量的数组对象也将导致编译错误。如下所示:,由于类型变量在运行时并不存在,而编译器无法确定实际类型是什么,因此编译器在粗体字代码出报错。,本章主要介绍了,JDK1.5,提供的泛型支持,还介绍了为何需要在编译时检查集合元素的类型,以及如何编程来实现这种检查,从而引出,JDK1.5,泛型给程序带来的简洁性和健壮性。本章详细讲解了如果定义泛型接口、泛型类,以及如何从泛型类、泛型接口派生子类或实现类,并深入讲解了泛型类的实质。本章介绍了类型通配符的用法,包括设定类型通配符的上限、下限等;本章重点介绍了泛型方法的知识,包括如何在方法签名是定义类型形参,以及泛型方法和类型通配符之间的区别和联系。本行最后介绍了,Java,不支持创建泛型数组,并深入分析了原因。,第七节 本章小结,
展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 图纸专区 > 小学资料


copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!