网友投稿 904 2025-04-01
疯狂Java学习笔记(47)-----------泛型详解
1. 概述
在引入范型之前,Java类型分为原始类型、复杂类型,其中复杂类型分为数组和类。引入范型后,一个复杂类型
就可以在细分成更多的类型。
例如原先的类型List,现在在细分成List, List等更多的类型。注意,现在List, List是两种不同的类型,他们之间没有继承关系,即使String继承了Object。下面的代码是非法的List ls = new ArrayList();List lo = ls;这样设计的原因在于,根据lo的声明,编译器允许你向lo中添加任意对象(例如Integer),但是此对象是List,破坏了数据类型的完整性。在引入范型之前,要在类中的方法支持多个数据类型,就需要对方法进行重载,在引入范型后,可以解决此问题(多态),更进一步可以定义多个参数以及返回值之间的关系。例如public void write(Integer i, Integer[] ia);public void write(Double d, Double[] da);的范型版本为public void write(T t, T[] ta);2. 定义&使用类型参数的命名风格为:推荐你用简练的名字作为形式类型参数的名字(如果可能,单个字符)。最好避免小写字母,这使它和其他的普通的形式参数很容易被区分开来。使用T代表类型,无论何时都没有比这更具体的类型来区分它。这经常见于泛型方法。如果有多个类型参数,我们可能使用字母表中T的临近的字母,比如S。如果一个泛型函数在一个泛型类里面出现,最好避免在方法的类型参数和类的类型参数中使用同样的名字来避免混淆。对内部类也是同样。2.1 定义带类型参数的类在定义带类型参数的类时,在紧跟类命之后的<>内,指定一个或多个类型参数的名字,同时也可以对类型参数的取值范围进行限定,多个类型参数之间用,号分隔。定义完类型参数后,可以在定义位置之后的类的几乎任意地方(静态块,静态属性,静态方法除外)使用类型参数,就像使用普通的类型一样。注意,父类定义的类型参数不能被子类继承。public class TestClassDefine {....}2.2 定义待类型参数方法在定义带类型参数的方法时,在紧跟可见范围修饰(例如public)之后的<>内,指定一个或多个类型参数的名字,同时也可以对类型参数的取值范围进行限定,多个类型参数之间用,号分隔。定义完类型参数后,可以在定义位置之后的方法的任意地方使用类型参数,就像使用普通的类型一样。例如:public T testGenericMethodDefine(T t, S s){...}注意:定义带类型参数的方法,骑主要目的是为了表达多个参数以及返回值之间的关系。例如本例子中T和S的继承关系, 返回值的类型和第一个类型参数的值相同。如果仅仅是想实现多态,请优先使用通配符解决。通配符的内容见下面章节。public void testGenericMethodDefine2(List s){...}应改为public void testGenericMethodDefine2(List<?> s){...}3. 类型参数赋值当对类或方法的类型参数进行赋值时,要求对所有的类型参数进行赋值。否则,将得到一个编译错误。3.1 对带类型参数的类进行类型参数赋值对带类型参数的类进行类型参数赋值有两种方式第一声明类变量或者实例化时。例如List list;list = new ArrayList;第二继承类或者实现接口时。例如public class MyList extends ArrayList implements List {...}3.2 对带类型参数方法进行赋值当调用范型方法时,编译器自动对类型参数进行赋值,当不能成功赋值时报编译错误。例如public T testGenericMethodDefine3(T t, List list){...}public T testGenericMethodDefine4(List list1, List list2){...}Number n = null;Integer i = null;Object o = null;testGenericMethodDefine(n, i);//此时T为Number, S为IntegertestGenericMethodDefine(o, i);//T为Object, S为IntegerList list1 = null;testGenericMethodDefine3(i, list1)//此时T为NumberList list2 = null;testGenericMethodDefine4(list1, list2)//编译报错3.3 通配符在上面两小节中,对是类型参数赋予具体的值,除此,还可以对类型参数赋予不确定值。例如List unknownList;List unknownNumberList;List unknownBaseLineIntgerList;注意: 在Java集合框架中,对于参数值是未知类型的容器类,只能读取其中元素,不能像其中添加元素,因为,其类型是未知,所以编译器无法识别添加元素的类型和容器的类型是否兼容,唯一的例外是NULLList listString;List unknownList2 = listString;unknownList = unknownList2;listString = unknownList;//编译错误4. 数组范型可以使用带范型参数值的类声明数组,却不可有创建数组List[] iListArray;new ArrayList[10];//编译时错误5. 实现原理5.1. Java范型时编译时技术,在运行时不包含范型信息,仅仅Class的实例中包含了类型参数的定义信息。泛型是通过java编译器的称为擦除(erasure)的前端处理来实现的。你可以(基本上就是)把它认为是一个从源码到源码的转换,它把泛型版本转换成非泛型版本。基本上,擦除去掉了所有的泛型类型信息。所有在尖括号之间的类型信息都被扔掉了,因此,比如说一个List类型被转换为List。所有对类型变量的引用被替换成类型变量的上限(通常是Object)。而且,无论何时结果代码类型不正确,会插入一个到合适类型的转换。 T badCast(T t, Object o) {return (T) o; // unchecked warning}类型参数在运行时并不存在。这意味着它们不会添加任何的时间或者空间上的负担,这很好。不幸的是,这也意味着你不能依靠他们进行类型转换。5.2.一个泛型类被其所有调用共享下面的代码打印的结果是什么?List l1 = new ArrayList();List l2 = new ArrayList();System.out.println(l1.getClass() == l2.getClass());或许你会说false,但是你想错了。它打印出true。因为一个泛型类的所有实例在运行时具有相同的运行时类(class),而不管他们的实际类型参数。事实上,泛型之所以叫泛型,就是因为它对所有其可能的类型参数,有同样的行为;同样的类可以被当作许多不同的类型。作为一个结果,类的静态变量和方法也在所有的实例间共享。这就是为什么在静态方法或静态初始化代码中或者在静态变量的声明和初始化时使用类型参数(类型参数是属于具体实例的)是不合法的原因。5.3. 转型和instanceof泛型类被所有其实例(instances)共享的另一个暗示是检查一个实例是不是一个特定类型的泛型类是没有意义的。Collection cs = new ArrayList();if (cs instanceof Collection) { ...} // 非法类似的,如下的类型转换Collection cstr = (Collection) cs;得到一个unchecked warning,因为运行时环境不会为你作这样的检查。6. Class的范型处理Java 5之后,Class变成范型化了。JDK1.5中一个变化是类 java.lang.Class是泛型化的。这是把泛型扩展到容器类之外的一个很有意思的例子。现在,Class有一个类型参数T, 你很可能会问,T 代表什么?它代表Class对象代表的类型。比如说,String.class类型代表 Class,Serializable.class代表 Class。这可以被用来提高你的反射代码的类型安全。特别的,因为 Class的 newInstance() 方法现在返回一个T, 你可以在使用反射创建对象时得到更精确的类型。比如说,假定你要写一个工具方法来进行一个数据库查询,给定一个SQL语句,并返回一个数据库中符合查询条件的对象集合(collection)。一个方法是显式的传递一个工厂对象,像下面的代码:interface Factory {public T[] make();}public Collection select(Factory factory, String statement) {Collection result = new ArrayList();/* run sql query using jdbc */for ( int i=0; i<10; i++ ) { /* iterate over jdbc results */T item = factory.make();/* use reflection and set all of item’s fields from sql results */result.add( item );}return result;}你可以这样调用:select(new Factory(){public EmpInfo make() {return new EmpInfo();}} , ”selection string”);也可以声明一个类 EmpInfoFactory 来支持接口 Factory:class EmpInfoFactory implements Factory { ...public EmpInfo make() { return new EmpInfo();}}然后调用:select(getMyEmpInfoFactory(), "selection string");这个解决方案的缺点是它需要下面的二者之一:调用处那冗长的匿名工厂类,或为每个要使用的类型声明一个工厂类并传递其对象给调用的地方这很不自然。使用class类型参数值是非常自然的,它可以被反射使用。没有泛型的代码可能是:Collection emps = sqlUtility.select(EmpInfo.class, ”select * from emps”); ...public static Collection select(Class c, String sqlStatement) {Collection result = new ArrayList();/* run sql query using jdbc */for ( /* iterate over jdbc results */ ) {Object item = c.newInstance();/* use reflection and set all of item’s fields from sql results */result.add(item);}return result;}但是这不能给我们返回一个我们要的精确类型的集合。现在Class是泛型的,我们可以写:Collection emps=sqlUtility.select(EmpInfo.class, ”select * from emps”); ...public static Collection select(Classc, String sqlStatement) {Collection result = new ArrayList();/* run sql query using jdbc */for ( /* iterate over jdbc results */ ) {T item = c.newInstance();/* use reflection and set all of item’s fields from sql results */result.add(item);}return result;}来通过一种类型安全的方式得到我们要的集合。这项技术是一个非常有用的技巧,它已成为一个在处理注释(annotations)的新API中被广泛使用的习惯用法。7. 新老代码兼容7.1. 为了保证代码的兼容性,下面的代码编译器(javac)允许,类型安全有你自己保证List l = new ArrayList();List l = new ArrayList();7.2. 在将你的类库升级为范型版本时,慎用协变式返回值。public class Foo {public Foo create(){return new Foo();}}public class Bar extends Foo {public Foo create(){return new Bar();}}采用协变式返回值风格,将Bar修改为public class Bar extends Foo {public Bar create(){return new Bar();}}要小心你类库的客户端。转自:http://blog.csdn.net/jinuxwu/article/details/6771121Java 容器 版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。 版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。 上一篇:excel表单元格移到(excel向后移单元格) 下一篇:wps怎么去掉文本框的框线? 相关文章 疯狂Java学习笔记(69)---------Lock">疯狂Java学习笔记(69)---------Lock 904 2025-04-01 疯狂Java学习笔记(54)----------详解Java IO(全面)">疯狂Java学习笔记(54)----------详解Java IO(全面) 904 2025-04-01 疯狂Java学习笔记(56)------------对象序列化">疯狂Java学习笔记(56)------------对象序列化 904 2025-04-01 推荐文章 企业生产管理是什么,企业生产管理软件进盘点进销存软件排行榜前十名进销存系统哪个简单好用?进销存系统优点工厂生产管理(工厂生产管理流程及制度)生产管理软件,机械制造业生产管理,制造业生产过程管理软件进销存软件和ERP有什么区别?进销存与erp软件理解进销存如何进行库存管理如何利用excel制作销售订单管理系统?数据库订单管理系统有哪些功能?数据库订单管理系统怎么设计?什么是数据库管理系统? 最近发表 南昌高三复读学校排名前十名 江科附中2025高复招生:分层锻造,特级名师领航一本线突围 九江高中高考复读学校口碑Top名单一览(2025年更新) Excel表格怎么做学生考试名单 Excel表格转换为Word文档的方法全解析 Excel 中将公式计算结果转换为纯数字的多种方法 Excel表格大小调整全攻略 如何在 Excel 表格中设置下拉选择内容 在 Excel 中固定表头并确保每页打印时都显示表头的方法详解 江西科技学院附属中学(江科附中)2025年高三复读班招生信息全解析 热评文章 零代码开发是什么?2022低代码平台排行榜">零代码开发是什么?2022低代码平台排行榜进销存库存管理系统(智慧进销存)">智能进销存库存管理系统(智慧进销存)在线文档哪家强?8款在线文档编辑软件推荐">在线文档哪家强?8款在线文档编辑软件推荐WPS2016怎么绘制简单的价格表?电子表格,居家办公更轻松">用在线电子表格,居家办公更轻松客户管理工具是什么?">客户管理工具是什么? 友情链接 伙伴云进销存管理低代码Excel表格FinClip
注意,现在List, List是两种不同的类型,他们之间没有继承关系,即使String继承了Object。下面的代码是非法的List ls = new ArrayList();List lo = ls;这样设计的原因在于,根据lo的声明,编译器允许你向lo中添加任意对象(例如Integer),但是此对象是List,破坏了数据类型的完整性。在引入范型之前,要在类中的方法支持多个数据类型,就需要对方法进行重载,在引入范型后,可以解决此问题(多态),更进一步可以定义多个参数以及返回值之间的关系。例如public void write(Integer i, Integer[] ia);public void write(Double d, Double[] da);的范型版本为public void write(T t, T[] ta);2. 定义&使用类型参数的命名风格为:推荐你用简练的名字作为形式类型参数的名字(如果可能,单个字符)。最好避免小写字母,这使它和其他的普通的形式参数很容易被区分开来。使用T代表类型,无论何时都没有比这更具体的类型来区分它。这经常见于泛型方法。如果有多个类型参数,我们可能使用字母表中T的临近的字母,比如S。如果一个泛型函数在一个泛型类里面出现,最好避免在方法的类型参数和类的类型参数中使用同样的名字来避免混淆。对内部类也是同样。2.1 定义带类型参数的类在定义带类型参数的类时,在紧跟类命之后的<>内,指定一个或多个类型参数的名字,同时也可以对类型参数的取值范围进行限定,多个类型参数之间用,号分隔。定义完类型参数后,可以在定义位置之后的类的几乎任意地方(静态块,静态属性,静态方法除外)使用类型参数,就像使用普通的类型一样。注意,父类定义的类型参数不能被子类继承。public class TestClassDefine {....}2.2 定义待类型参数方法在定义带类型参数的方法时,在紧跟可见范围修饰(例如public)之后的<>内,指定一个或多个类型参数的名字,同时也可以对类型参数的取值范围进行限定,多个类型参数之间用,号分隔。定义完类型参数后,可以在定义位置之后的方法的任意地方使用类型参数,就像使用普通的类型一样。例如:public T testGenericMethodDefine(T t, S s){...}注意:定义带类型参数的方法,骑主要目的是为了表达多个参数以及返回值之间的关系。例如本例子中T和S的继承关系, 返回值的类型和第一个类型参数的值相同。如果仅仅是想实现多态,请优先使用通配符解决。通配符的内容见下面章节。public void testGenericMethodDefine2(List s){...}应改为public void testGenericMethodDefine2(List<?> s){...}3. 类型参数赋值当对类或方法的类型参数进行赋值时,要求对所有的类型参数进行赋值。否则,将得到一个编译错误。3.1 对带类型参数的类进行类型参数赋值对带类型参数的类进行类型参数赋值有两种方式第一声明类变量或者实例化时。例如List list;list = new ArrayList;第二继承类或者实现接口时。例如public class MyList extends ArrayList implements List {...}3.2 对带类型参数方法进行赋值当调用范型方法时,编译器自动对类型参数进行赋值,当不能成功赋值时报编译错误。例如public T testGenericMethodDefine3(T t, List list){...}public T testGenericMethodDefine4(List list1, List list2){...}Number n = null;Integer i = null;Object o = null;testGenericMethodDefine(n, i);//此时T为Number, S为IntegertestGenericMethodDefine(o, i);//T为Object, S为IntegerList list1 = null;testGenericMethodDefine3(i, list1)//此时T为NumberList list2 = null;testGenericMethodDefine4(list1, list2)//编译报错3.3 通配符在上面两小节中,对是类型参数赋予具体的值,除此,还可以对类型参数赋予不确定值。例如List unknownList;List unknownNumberList;List unknownBaseLineIntgerList;注意: 在Java集合框架中,对于参数值是未知类型的容器类,只能读取其中元素,不能像其中添加元素,因为,其类型是未知,所以编译器无法识别添加元素的类型和容器的类型是否兼容,唯一的例外是NULLList listString;List unknownList2 = listString;unknownList = unknownList2;listString = unknownList;//编译错误4. 数组范型可以使用带范型参数值的类声明数组,却不可有创建数组List[] iListArray;new ArrayList[10];//编译时错误5. 实现原理5.1. Java范型时编译时技术,在运行时不包含范型信息,仅仅Class的实例中包含了类型参数的定义信息。泛型是通过java编译器的称为擦除(erasure)的前端处理来实现的。你可以(基本上就是)把它认为是一个从源码到源码的转换,它把泛型版本转换成非泛型版本。基本上,擦除去掉了所有的泛型类型信息。所有在尖括号之间的类型信息都被扔掉了,因此,比如说一个List类型被转换为List。所有对类型变量的引用被替换成类型变量的上限(通常是Object)。而且,无论何时结果代码类型不正确,会插入一个到合适类型的转换。 T badCast(T t, Object o) {return (T) o; // unchecked warning}类型参数在运行时并不存在。这意味着它们不会添加任何的时间或者空间上的负担,这很好。不幸的是,这也意味着你不能依靠他们进行类型转换。5.2.一个泛型类被其所有调用共享下面的代码打印的结果是什么?List l1 = new ArrayList();List l2 = new ArrayList();System.out.println(l1.getClass() == l2.getClass());或许你会说false,但是你想错了。它打印出true。因为一个泛型类的所有实例在运行时具有相同的运行时类(class),而不管他们的实际类型参数。事实上,泛型之所以叫泛型,就是因为它对所有其可能的类型参数,有同样的行为;同样的类可以被当作许多不同的类型。作为一个结果,类的静态变量和方法也在所有的实例间共享。这就是为什么在静态方法或静态初始化代码中或者在静态变量的声明和初始化时使用类型参数(类型参数是属于具体实例的)是不合法的原因。5.3. 转型和instanceof泛型类被所有其实例(instances)共享的另一个暗示是检查一个实例是不是一个特定类型的泛型类是没有意义的。Collection cs = new ArrayList();if (cs instanceof Collection) { ...} // 非法类似的,如下的类型转换Collection cstr = (Collection) cs;得到一个unchecked warning,因为运行时环境不会为你作这样的检查。6. Class的范型处理Java 5之后,Class变成范型化了。JDK1.5中一个变化是类 java.lang.Class是泛型化的。这是把泛型扩展到容器类之外的一个很有意思的例子。现在,Class有一个类型参数T, 你很可能会问,T 代表什么?它代表Class对象代表的类型。比如说,String.class类型代表 Class,Serializable.class代表 Class。这可以被用来提高你的反射代码的类型安全。特别的,因为 Class的 newInstance() 方法现在返回一个T, 你可以在使用反射创建对象时得到更精确的类型。比如说,假定你要写一个工具方法来进行一个数据库查询,给定一个SQL语句,并返回一个数据库中符合查询条件的对象集合(collection)。一个方法是显式的传递一个工厂对象,像下面的代码:interface Factory {public T[] make();}public Collection select(Factory factory, String statement) {Collection result = new ArrayList();/* run sql query using jdbc */for ( int i=0; i<10; i++ ) { /* iterate over jdbc results */T item = factory.make();/* use reflection and set all of item’s fields from sql results */result.add( item );}return result;}你可以这样调用:select(new Factory(){public EmpInfo make() {return new EmpInfo();}} , ”selection string”);也可以声明一个类 EmpInfoFactory 来支持接口 Factory:class EmpInfoFactory implements Factory { ...public EmpInfo make() { return new EmpInfo();}}然后调用:select(getMyEmpInfoFactory(), "selection string");这个解决方案的缺点是它需要下面的二者之一:调用处那冗长的匿名工厂类,或为每个要使用的类型声明一个工厂类并传递其对象给调用的地方这很不自然。使用class类型参数值是非常自然的,它可以被反射使用。没有泛型的代码可能是:Collection emps = sqlUtility.select(EmpInfo.class, ”select * from emps”); ...public static Collection select(Class c, String sqlStatement) {Collection result = new ArrayList();/* run sql query using jdbc */for ( /* iterate over jdbc results */ ) {Object item = c.newInstance();/* use reflection and set all of item’s fields from sql results */result.add(item);}return result;}但是这不能给我们返回一个我们要的精确类型的集合。现在Class是泛型的,我们可以写:Collection emps=sqlUtility.select(EmpInfo.class, ”select * from emps”); ...public static Collection select(Classc, String sqlStatement) {Collection result = new ArrayList();/* run sql query using jdbc */for ( /* iterate over jdbc results */ ) {T item = c.newInstance();/* use reflection and set all of item’s fields from sql results */result.add(item);}return result;}来通过一种类型安全的方式得到我们要的集合。这项技术是一个非常有用的技巧,它已成为一个在处理注释(annotations)的新API中被广泛使用的习惯用法。7. 新老代码兼容7.1. 为了保证代码的兼容性,下面的代码编译器(javac)允许,类型安全有你自己保证List l = new ArrayList();List l = new ArrayList();7.2. 在将你的类库升级为范型版本时,慎用协变式返回值。public class Foo {public Foo create(){return new Foo();}}public class Bar extends Foo {public Foo create(){return new Bar();}}采用协变式返回值风格,将Bar修改为public class Bar extends Foo {public Bar create(){return new Bar();}}要小心你类库的客户端。转自:http://blog.csdn.net/jinuxwu/article/details/6771121Java 容器 版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。 版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。 上一篇:excel表单元格移到(excel向后移单元格) 下一篇:wps怎么去掉文本框的框线? 相关文章 疯狂Java学习笔记(69)---------Lock">疯狂Java学习笔记(69)---------Lock 904 2025-04-01 疯狂Java学习笔记(54)----------详解Java IO(全面)">疯狂Java学习笔记(54)----------详解Java IO(全面) 904 2025-04-01 疯狂Java学习笔记(56)------------对象序列化">疯狂Java学习笔记(56)------------对象序列化 904 2025-04-01 推荐文章 企业生产管理是什么,企业生产管理软件进盘点进销存软件排行榜前十名进销存系统哪个简单好用?进销存系统优点工厂生产管理(工厂生产管理流程及制度)生产管理软件,机械制造业生产管理,制造业生产过程管理软件进销存软件和ERP有什么区别?进销存与erp软件理解进销存如何进行库存管理如何利用excel制作销售订单管理系统?数据库订单管理系统有哪些功能?数据库订单管理系统怎么设计?什么是数据库管理系统? 最近发表 南昌高三复读学校排名前十名 江科附中2025高复招生:分层锻造,特级名师领航一本线突围 九江高中高考复读学校口碑Top名单一览(2025年更新) Excel表格怎么做学生考试名单 Excel表格转换为Word文档的方法全解析 Excel 中将公式计算结果转换为纯数字的多种方法 Excel表格大小调整全攻略 如何在 Excel 表格中设置下拉选择内容 在 Excel 中固定表头并确保每页打印时都显示表头的方法详解 江西科技学院附属中学(江科附中)2025年高三复读班招生信息全解析 热评文章 零代码开发是什么?2022低代码平台排行榜">零代码开发是什么?2022低代码平台排行榜进销存库存管理系统(智慧进销存)">智能进销存库存管理系统(智慧进销存)在线文档哪家强?8款在线文档编辑软件推荐">在线文档哪家强?8款在线文档编辑软件推荐WPS2016怎么绘制简单的价格表?电子表格,居家办公更轻松">用在线电子表格,居家办公更轻松客户管理工具是什么?">客户管理工具是什么? 友情链接 伙伴云进销存管理低代码Excel表格FinClip
他们之间没有继承关系,即使String继承了Object。下面的代码是非法的
List ls = new ArrayList();
List lo = ls;这样设计的原因在于,根据lo的声明,编译器允许你向lo中添加任意对象(例如Integer),但是此对象是List,破坏了数据类型的完整性。在引入范型之前,要在类中的方法支持多个数据类型,就需要对方法进行重载,在引入范型后,可以解决此问题(多态),更进一步可以定义多个参数以及返回值之间的关系。例如public void write(Integer i, Integer[] ia);public void write(Double d, Double[] da);的范型版本为public void write(T t, T[] ta);2. 定义&使用类型参数的命名风格为:推荐你用简练的名字作为形式类型参数的名字(如果可能,单个字符)。最好避免小写字母,这使它和其他的普通的形式参数很容易被区分开来。使用T代表类型,无论何时都没有比这更具体的类型来区分它。这经常见于泛型方法。如果有多个类型参数,我们可能使用字母表中T的临近的字母,比如S。如果一个泛型函数在一个泛型类里面出现,最好避免在方法的类型参数和类的类型参数中使用同样的名字来避免混淆。对内部类也是同样。2.1 定义带类型参数的类在定义带类型参数的类时,在紧跟类命之后的<>内,指定一个或多个类型参数的名字,同时也可以对类型参数的取值范围进行限定,多个类型参数之间用,号分隔。定义完类型参数后,可以在定义位置之后的类的几乎任意地方(静态块,静态属性,静态方法除外)使用类型参数,就像使用普通的类型一样。注意,父类定义的类型参数不能被子类继承。public class TestClassDefine {....}2.2 定义待类型参数方法在定义带类型参数的方法时,在紧跟可见范围修饰(例如public)之后的<>内,指定一个或多个类型参数的名字,同时也可以对类型参数的取值范围进行限定,多个类型参数之间用,号分隔。定义完类型参数后,可以在定义位置之后的方法的任意地方使用类型参数,就像使用普通的类型一样。例如:public T testGenericMethodDefine(T t, S s){...}注意:定义带类型参数的方法,骑主要目的是为了表达多个参数以及返回值之间的关系。例如本例子中T和S的继承关系, 返回值的类型和第一个类型参数的值相同。如果仅仅是想实现多态,请优先使用通配符解决。通配符的内容见下面章节。public void testGenericMethodDefine2(List s){...}应改为public void testGenericMethodDefine2(List<?> s){...}3. 类型参数赋值当对类或方法的类型参数进行赋值时,要求对所有的类型参数进行赋值。否则,将得到一个编译错误。3.1 对带类型参数的类进行类型参数赋值对带类型参数的类进行类型参数赋值有两种方式第一声明类变量或者实例化时。例如List list;list = new ArrayList;第二继承类或者实现接口时。例如public class MyList extends ArrayList implements List {...}3.2 对带类型参数方法进行赋值当调用范型方法时,编译器自动对类型参数进行赋值,当不能成功赋值时报编译错误。例如public T testGenericMethodDefine3(T t, List list){...}public T testGenericMethodDefine4(List list1, List list2){...}Number n = null;Integer i = null;Object o = null;testGenericMethodDefine(n, i);//此时T为Number, S为IntegertestGenericMethodDefine(o, i);//T为Object, S为IntegerList list1 = null;testGenericMethodDefine3(i, list1)//此时T为NumberList list2 = null;testGenericMethodDefine4(list1, list2)//编译报错3.3 通配符在上面两小节中,对是类型参数赋予具体的值,除此,还可以对类型参数赋予不确定值。例如List unknownList;List unknownNumberList;List unknownBaseLineIntgerList;注意: 在Java集合框架中,对于参数值是未知类型的容器类,只能读取其中元素,不能像其中添加元素,因为,其类型是未知,所以编译器无法识别添加元素的类型和容器的类型是否兼容,唯一的例外是NULLList listString;List unknownList2 = listString;unknownList = unknownList2;listString = unknownList;//编译错误4. 数组范型可以使用带范型参数值的类声明数组,却不可有创建数组List[] iListArray;new ArrayList[10];//编译时错误5. 实现原理5.1. Java范型时编译时技术,在运行时不包含范型信息,仅仅Class的实例中包含了类型参数的定义信息。泛型是通过java编译器的称为擦除(erasure)的前端处理来实现的。你可以(基本上就是)把它认为是一个从源码到源码的转换,它把泛型版本转换成非泛型版本。基本上,擦除去掉了所有的泛型类型信息。所有在尖括号之间的类型信息都被扔掉了,因此,比如说一个List类型被转换为List。所有对类型变量的引用被替换成类型变量的上限(通常是Object)。而且,无论何时结果代码类型不正确,会插入一个到合适类型的转换。 T badCast(T t, Object o) {return (T) o; // unchecked warning}类型参数在运行时并不存在。这意味着它们不会添加任何的时间或者空间上的负担,这很好。不幸的是,这也意味着你不能依靠他们进行类型转换。5.2.一个泛型类被其所有调用共享下面的代码打印的结果是什么?List l1 = new ArrayList();List l2 = new ArrayList();System.out.println(l1.getClass() == l2.getClass());或许你会说false,但是你想错了。它打印出true。因为一个泛型类的所有实例在运行时具有相同的运行时类(class),而不管他们的实际类型参数。事实上,泛型之所以叫泛型,就是因为它对所有其可能的类型参数,有同样的行为;同样的类可以被当作许多不同的类型。作为一个结果,类的静态变量和方法也在所有的实例间共享。这就是为什么在静态方法或静态初始化代码中或者在静态变量的声明和初始化时使用类型参数(类型参数是属于具体实例的)是不合法的原因。5.3. 转型和instanceof泛型类被所有其实例(instances)共享的另一个暗示是检查一个实例是不是一个特定类型的泛型类是没有意义的。Collection cs = new ArrayList();if (cs instanceof Collection) { ...} // 非法类似的,如下的类型转换Collection cstr = (Collection) cs;得到一个unchecked warning,因为运行时环境不会为你作这样的检查。6. Class的范型处理Java 5之后,Class变成范型化了。JDK1.5中一个变化是类 java.lang.Class是泛型化的。这是把泛型扩展到容器类之外的一个很有意思的例子。现在,Class有一个类型参数T, 你很可能会问,T 代表什么?它代表Class对象代表的类型。比如说,String.class类型代表 Class,Serializable.class代表 Class。这可以被用来提高你的反射代码的类型安全。特别的,因为 Class的 newInstance() 方法现在返回一个T, 你可以在使用反射创建对象时得到更精确的类型。比如说,假定你要写一个工具方法来进行一个数据库查询,给定一个SQL语句,并返回一个数据库中符合查询条件的对象集合(collection)。一个方法是显式的传递一个工厂对象,像下面的代码:interface Factory {public T[] make();}public Collection select(Factory factory, String statement) {Collection result = new ArrayList();/* run sql query using jdbc */for ( int i=0; i<10; i++ ) { /* iterate over jdbc results */T item = factory.make();/* use reflection and set all of item’s fields from sql results */result.add( item );}return result;}你可以这样调用:select(new Factory(){public EmpInfo make() {return new EmpInfo();}} , ”selection string”);也可以声明一个类 EmpInfoFactory 来支持接口 Factory:class EmpInfoFactory implements Factory { ...public EmpInfo make() { return new EmpInfo();}}然后调用:select(getMyEmpInfoFactory(), "selection string");这个解决方案的缺点是它需要下面的二者之一:调用处那冗长的匿名工厂类,或为每个要使用的类型声明一个工厂类并传递其对象给调用的地方这很不自然。使用class类型参数值是非常自然的,它可以被反射使用。没有泛型的代码可能是:Collection emps = sqlUtility.select(EmpInfo.class, ”select * from emps”); ...public static Collection select(Class c, String sqlStatement) {Collection result = new ArrayList();/* run sql query using jdbc */for ( /* iterate over jdbc results */ ) {Object item = c.newInstance();/* use reflection and set all of item’s fields from sql results */result.add(item);}return result;}但是这不能给我们返回一个我们要的精确类型的集合。现在Class是泛型的,我们可以写:Collection emps=sqlUtility.select(EmpInfo.class, ”select * from emps”); ...public static Collection select(Classc, String sqlStatement) {Collection result = new ArrayList();/* run sql query using jdbc */for ( /* iterate over jdbc results */ ) {T item = c.newInstance();/* use reflection and set all of item’s fields from sql results */result.add(item);}return result;}来通过一种类型安全的方式得到我们要的集合。这项技术是一个非常有用的技巧,它已成为一个在处理注释(annotations)的新API中被广泛使用的习惯用法。7. 新老代码兼容7.1. 为了保证代码的兼容性,下面的代码编译器(javac)允许,类型安全有你自己保证List l = new ArrayList();List l = new ArrayList();7.2. 在将你的类库升级为范型版本时,慎用协变式返回值。public class Foo {public Foo create(){return new Foo();}}public class Bar extends Foo {public Foo create(){return new Bar();}}采用协变式返回值风格,将Bar修改为public class Bar extends Foo {public Bar create(){return new Bar();}}要小心你类库的客户端。转自:http://blog.csdn.net/jinuxwu/article/details/6771121Java 容器 版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。 版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。 上一篇:excel表单元格移到(excel向后移单元格) 下一篇:wps怎么去掉文本框的框线? 相关文章 疯狂Java学习笔记(69)---------Lock">疯狂Java学习笔记(69)---------Lock 904 2025-04-01 疯狂Java学习笔记(54)----------详解Java IO(全面)">疯狂Java学习笔记(54)----------详解Java IO(全面) 904 2025-04-01 疯狂Java学习笔记(56)------------对象序列化">疯狂Java学习笔记(56)------------对象序列化 904 2025-04-01 推荐文章 企业生产管理是什么,企业生产管理软件进盘点进销存软件排行榜前十名进销存系统哪个简单好用?进销存系统优点工厂生产管理(工厂生产管理流程及制度)生产管理软件,机械制造业生产管理,制造业生产过程管理软件进销存软件和ERP有什么区别?进销存与erp软件理解进销存如何进行库存管理如何利用excel制作销售订单管理系统?数据库订单管理系统有哪些功能?数据库订单管理系统怎么设计?什么是数据库管理系统? 最近发表 南昌高三复读学校排名前十名 江科附中2025高复招生:分层锻造,特级名师领航一本线突围 九江高中高考复读学校口碑Top名单一览(2025年更新) Excel表格怎么做学生考试名单 Excel表格转换为Word文档的方法全解析 Excel 中将公式计算结果转换为纯数字的多种方法 Excel表格大小调整全攻略 如何在 Excel 表格中设置下拉选择内容 在 Excel 中固定表头并确保每页打印时都显示表头的方法详解 江西科技学院附属中学(江科附中)2025年高三复读班招生信息全解析 热评文章 零代码开发是什么?2022低代码平台排行榜">零代码开发是什么?2022低代码平台排行榜进销存库存管理系统(智慧进销存)">智能进销存库存管理系统(智慧进销存)在线文档哪家强?8款在线文档编辑软件推荐">在线文档哪家强?8款在线文档编辑软件推荐WPS2016怎么绘制简单的价格表?电子表格,居家办公更轻松">用在线电子表格,居家办公更轻松客户管理工具是什么?">客户管理工具是什么? 友情链接 伙伴云进销存管理低代码Excel表格FinClip
这样设计的原因在于,根据lo的声明,编译器允许你向lo中添加任意对象(例如Integer),但是此对象是
List,破坏了数据类型的完整性。
在引入范型之前,要在类中的方法支持多个数据类型,就需要对方法进行重载,在引入范型后,可以解决此问题
(多态),更进一步可以定义多个参数以及返回值之间的关系。
例如
public void write(Integer i, Integer[] ia);
public void write(Double d, Double[] da);
的范型版本为
public void write(T t, T[] ta);
2. 定义&使用
类型参数的命名风格为:
推荐你用简练的名字作为形式类型参数的名字(如果可能,单个字符)。最好避免小写字母,这使它和其他的普通
的形式参数很容易被区分开来。
使用T代表类型,无论何时都没有比这更具体的类型来区分它。这经常见于泛型方法。如果有多个类型参数,我们
可能使用字母表中T的临近的字母,比如S。
如果一个泛型函数在一个泛型类里面出现,最好避免在方法的类型参数和类的类型参数中使用同样的名字来避免混
淆。对内部类也是同样。
2.1 定义带类型参数的类
在定义带类型参数的类时,在紧跟类命之后的<>内,指定一个或多个类型参数的名字,同时也可以对类型参数的取
值范围进行限定,多个类型参数之间用,号分隔。
定义完类型参数后,可以在定义位置之后的类的几乎任意地方(静态块,静态属性,静态方法除外)使用类型参数,
就像使用普通的类型一样。
注意,父类定义的类型参数不能被子类继承。
public class TestClassDefine {
....
}
2.2 定义待类型参数方法
在定义带类型参数的方法时,在紧跟可见范围修饰(例如public)之后的<>内,指定一个或多个类型参数的名字,
同时也可以对类型参数的取值范围进行限定,多个类型参数之间用,号分隔。
定义完类型参数后,可以在定义位置之后的方法的任意地方使用类型参数,就像使用普通的类型一样。
例如:
public T testGenericMethodDefine(T t, S s){
...
注意:定义带类型参数的方法,骑主要目的是为了表达多个参数以及返回值之间的关系。例如本例子中T和S的继
承关系, 返回值的类型和第一个类型参数的值相同。
如果仅仅是想实现多态,请优先使用通配符解决。通配符的内容见下面章节。
public void testGenericMethodDefine2(List s){
应改为
public void testGenericMethodDefine2(List<?> s){
3. 类型参数赋值
当对类或方法的类型参数进行赋值时,要求对所有的类型参数进行赋值。否则,将得到一个编译错误。
3.1 对带类型参数的类进行类型参数赋值
对带类型参数的类进行类型参数赋值有两种方式
第一声明类变量或者实例化时。例如
List list;
list = new ArrayList;
第二继承类或者实现接口时。例如
public class MyList extends ArrayList implements List {...}
3.2 对带类型参数方法进行赋值
当调用范型方法时,编译器自动对类型参数进行赋值,当不能成功赋值时报编译错误。例如
public T testGenericMethodDefine3(T t, List list){
public T testGenericMethodDefine4(List list1, List list2){
Number n = null;
Integer i = null;
Object o = null;
testGenericMethodDefine(n, i);//此时T为Number, S为Integer
testGenericMethodDefine(o, i);//T为Object, S为Integer
List list1 = null;
testGenericMethodDefine3(i, list1)//此时T为Number
List list2 = null;
testGenericMethodDefine4(list1, list2)//编译报错
3.3 通配符
在上面两小节中,对是类型参数赋予具体的值,除此,还可以对类型参数赋予不确定值。例如
List unknownList;
List unknownNumberList;
List unknownBaseLineIntgerList;
注意: 在Java集合框架中,对于参数值是未知类型的容器类,只能读取其中元素,不能像其中添加元素,
因为,其类型是未知,所以编译器无法识别添加元素的类型和容器的类型是否兼容,唯一的例外是NULL
List listString;
List unknownList2 = listString;
unknownList = unknownList2;
listString = unknownList;//编译错误
4. 数组范型
可以使用带范型参数值的类声明数组,却不可有创建数组
List[] iListArray;
new ArrayList[10];//编译时错误
5. 实现原理
5.1. Java范型时编译时技术,在运行时不包含范型信息,仅仅Class的实例中包含了类型参数的定义信息。
泛型是通过java编译器的称为擦除(erasure)的前端处理来实现的。你可以(基本上就是)把它认为是一个从源
码到源码的转换,它把泛型版本转换成非泛型版本。
基本上,擦除去掉了所有的泛型类型信息。所有在尖括号之间的类型信息都被扔掉了,因此,比如说一个
List类型被转换为List。所有对类型变量的引用被替换成类型变量的上限(通常是Object)。而且,
无论何时结果代码类型不正确,会插入一个到合适类型的转换。
T badCast(T t, Object o) {
return (T) o; // unchecked warning
类型参数在运行时并不存在。这意味着它们不会添加任何的时间或者空间上的负担,这很好。不幸的是,这也意味
着你不能依靠他们进行类型转换。
5.2.一个泛型类被其所有调用共享
下面的代码打印的结果是什么?
List l1 = new ArrayList();
List l2 = new ArrayList();
System.out.println(l1.getClass() == l2.getClass());
或许你会说false,但是你想错了。它打印出true。因为一个泛型类的所有实例在运行时具有相同的运行时类(class),
而不管他们的实际类型参数。
事实上,泛型之所以叫泛型,就是因为它对所有其可能的类型参数,有同样的行为;同样的类可以被当作许多不同
的类型。作为一个结果,类的静态变量和方法也在所有的实例间共享。这就是为什么在静态方法或静态初始化代码
中或者在静态变量的声明和初始化时使用类型参数(类型参数是属于具体实例的)是不合法的原因。
5.3. 转型和instanceof
泛型类被所有其实例(instances)共享的另一个暗示是检查一个实例是不是一个特定类型的泛型类是没有意义的。
Collection cs = new ArrayList();
if (cs instanceof Collection) { ...} // 非法
类似的,如下的类型转换
Collection cstr = (Collection) cs;
得到一个unchecked warning,因为运行时环境不会为你作这样的检查。
6. Class的范型处理
Java 5之后,Class变成范型化了。
JDK1.5中一个变化是类 java.lang.Class是泛型化的。这是把泛型扩展到容器类之外的一个很有意思的例子。
现在,Class有一个类型参数T, 你很可能会问,T 代表什么?它代表Class对象代表的类型。比如说,
String.class类型代表 Class,Serializable.class代表 Class。
这可以被用来提高你的反射代码的类型安全。
特别的,因为 Class的 newInstance() 方法现在返回一个T, 你可以在使用反射创建对象时得到更精确的类型。
比如说,假定你要写一个工具方法来进行一个数据库查询,给定一个SQL语句,并返回一个数据库中符合查询条件
的对象集合(collection)。
一个方法是显式的传递一个工厂对象,像下面的代码:
interface Factory {
public T[] make();
public Collection select(Factory factory, String statement) {
Collection result = new ArrayList();
/* run sql query using jdbc */
for ( int i=0; i<10; i++ ) { /* iterate over jdbc results */
T item = factory.make();
/* use reflection and set all of item’s fields from sql results */
result.add( item );
return result;
你可以这样调用:
select(new Factory(){
public EmpInfo make() {
return new EmpInfo();
} , ”selection string”);
也可以声明一个类 EmpInfoFactory 来支持接口 Factory:
class EmpInfoFactory implements Factory { ...
public EmpInfo make() { return new EmpInfo();}
然后调用:
select(getMyEmpInfoFactory(), "selection string");
这个解决方案的缺点是它需要下面的二者之一:
调用处那冗长的匿名工厂类,或为每个要使用的类型声明一个工厂类并传递其对象给调用的地方
这很不自然。
使用class类型参数值是非常自然的,它可以被反射使用。没有泛型的代码可能是:
Collection emps = sqlUtility.select(EmpInfo.class, ”select * from emps”); ...
public static Collection select(Class c, String sqlStatement) {
for ( /* iterate over jdbc results */ ) {
Object item = c.newInstance();
result.add(item);
但是这不能给我们返回一个我们要的精确类型的集合。现在Class是泛型的,我们可以写:
Collection emps=sqlUtility.select(EmpInfo.class, ”select * from emps”); ...
public static Collection select(Classc, String sqlStatement) {
T item = c.newInstance();
来通过一种类型安全的方式得到我们要的集合。
这项技术是一个非常有用的技巧,它已成为一个在处理注释(annotations)的新API中被广泛使用的习惯用法。
7. 新老代码兼容
7.1. 为了保证代码的兼容性,下面的代码编译器(javac)允许,类型安全有你自己保证
List l = new ArrayList();
7.2. 在将你的类库升级为范型版本时,慎用协变式返回值。
public class Foo {
public Foo create(){
return new Foo();
public class Bar extends Foo {
return new Bar();
采用协变式返回值风格,将Bar修改为
public Bar create(){
要小心你类库的客户端。
转自:http://blog.csdn.net/jinuxwu/article/details/6771121
Java 容器
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。