




已阅读5页,还剩10页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、泛型(Generics)泛型是JDK1.5中一个最“酷”的特征。通过引入泛型,我们将获得编译时类型的安全和运行时更小地抛出ClassCastExceptions的可能。在JDK1.5中,你可以声明一个集合将接收/返回的对象的类型。在JDK1.4中,创建雇员名字的清单(List)需要一个集合对象,像下面的语句:List listOfEmployeeName = new ArrayList();在JDK1.5中,你将使用下面语句List listOfEmployeeName = new ArrayList();最“酷”的是,如果你试图插入非string类型的值,你将在编译时发现并且修正这类问题。没有泛型,你会发现这样一个bug,当你的客户调用后会告诉你,你所编写的程序抛出ClassCastException异常而崩溃。另外,当你从集合中得到一个元素时你无需进行强制转换。故原先为:String employeeName = (String) listOfEmployee.get(i);而下面的语句将比上面的更加简单:String employeeName = listOfEmployee.get(i);不清楚对象的类型而强制转换对象是不合理的,并且更重要的是,它将在运行时失败。假使用户无意间传入一个包含string buffers类型而非string类型的集合,那结果会怎样呢。在Listing A中,客户被要求传入一个编译器无法强制的strings类型集合。Listing B中显示了同样的方法使用泛型是如何实现的。Listing AstaticbooleancheckName(Collection employeeNameList, String name) for (Iteratori = employeeNamList.iterator(); i.hasNext(); ) String s = (String) i.next();if(s.equals(name)return true;/print employee name here .return false;Listing BstaticbooleancheckName(Collection employeeNameList, String name) for (Iteratori = employeeNamList.iterator(); i.hasNext(); ) if(i.next().equals(name)return true;/print employee name here .return false;现在,通过方法签名可以清楚知道输入集合必须只能包含strings。如果客户试图传入一个包含string buffers的集合,程序将不会编译。同时注意,该方法不包含任何强制转换。它只需要短短一行,一旦你习惯泛型后,它也更加清晰。2、在JDK当前版本下的For循环语法如下:void printAll(Collection c) for (Iteratori = c.iterator(); i.hasNext(); ) Employee emp = (Employee)i.next();System.out.println(emp.getName();现在,用增强的For语句实现相同方法:voidprintAll(Collection c) for (Object o : c)System.out.println(TimerTask)o).getName();在这类For循环中,你应该将:看成in,所以,在该例中可以看成for Object o in c。你可以发现这种For循环更具可读性。3、自动置入/自动取出(Autoboxing/unboxing)Java有基本数据类型,在这些基本数据类型周围又有包装类。通常,编程人员需要将一种类型转换成另一种。看看Listing C.中的代码片断。Listing Cpublic class Employee private static final Integer CHILD = new Integer(0);public static void main(String args) /code for adding n to an Integerint n=10;Integer age= new Integer(30);Integer ageAfterTenYear= new Integer(Value +10);请注意,用于计算ageAfterTenYear的内循环代码看上去是多么杂乱。现在,在Listing D.中看看相同的程序使用autoboxing重写后的样子。Listing Dpublic class Employee public static void main(String args) int n=10;Integer age= new Integer(30);Integer ageAfterTenYear= age +10;有一件事值得注意的:在先前,如果你取出(unbox)Null值,它将变为0。在次代码中,编译器将自动地转换Integer为int然后加上10,接着将其转换回Integer.。4、类型安全的枚举(Typesafeenums)类型安全枚举提供下列特性:他们提供编译时类型安全。他们都是对象,因此你不需要将他们放入集合中。他们作为一种类的实现,因此你可以添加一些方法。他们为枚举类型提供了合适的命名空间。他们打印的值具有情报性(informative) 如果你打印一个整数枚举(intenum),你只是看见一个数字,它可能并不具有情报性。例一:enum Season winter, spring, summer, fall 例二:public enum Coin penny(1), nickel(5), dime(10), quarter(25);Coin(int value) this.value = value; private final int value;public int value() return value; 5、静态导入(Static import)静态导入使代码更易读。通常,你要使用定义在另一个类中的常量(constants),像这样:importorg.yyy.pkg.Increment;class Employee public Double calculateSalary(Double salaryreturn salary + Increment.INCREMENT * salary;当时使用静态导入,我们无需为常量名前缀类名就能使用这些常量,像这样:import static org.yyy.pkg.Increment;class Employee public Double calculateSalary(Double salaryreturn salary + INCREMENT * salary;注意,我们可以调用INCREMENT这一常量而不要使用类名Increment.。6、元数据(Metadata)元数据特征志于使开发者们借助厂商提供的工具可以进行更简易的开发。看一看Listing E.中的代码。Listing Eimportorg.yyy.hr;public interface EmployeeI extends Java.rmi.Remote public String getName()throwsJava.rmi.RemoteException;public String getLocation ()throwsJava.rmi.RemoteException;public class EmployeeImpl implements EmployeeI public String getName()public String getLocation ()通过元数据的支持,你可以改写Listing E中的代码为:importorg.yyy.hr;public class Employee Remote public String getName() .Remote public public String getLocation() .JDK1.5新特性之Java Generics在JDK1.5之前的版本中,对于一个Collection类库中的容器类实例,可将任意类型对象加入其中(都被当作Object实例看待);从容器中取出的对象也只是一个Object实例,需要将其强制转型为期待的类型,这种强制转型的运行时正确性由程序员自行保证。例如以下代码片断:List intList = new ArrayList(); /创建一个List,准备存放一些Integer实例intList.add(new Integer(0);intList.add(“1”); /不小心加入了一个字符串;但在编译和运行时都不报错,只有仔细的代码走 /才能揪出Integer i0 = (Integer)intList.get(0);Integer i1 = (Integer)intList.get(1); /编译通过,直到运行时才抛ClassCastException 而在JDK1.5中,可以创建一个明确只能存放某种特定类型对象的容器类实例,例如如下代码:List intList = new ArrayList(); /intList为存放Integer实例的ListintList.add(new Integer(0);Integer i0 = intList.get(0); /无需(Integer)强制转型;List的get()返回的就是Integer类 /型对象intList.add(“1”); /编译不通过,因为List的add()方法只接受Integer类型的参数 “List intList = new ArrayList();”就是最简单且最常用的Generic应用;显然,运用Generic后的代码更具可读性和健壮性。2 Generic类JDK1.5中Collection类库的大部分类都被改进为Generic类。以下是从JDK1.5源码中截取的关于List和Iterator接口定义的代码片断:public interface List void add(E x); Iterator iterator;public interface Iterator E next(); boolean hasNext();以List为例,“public interface List”中的E是List的类型参数,用户在使用List时可为类型参数指定一个确定类型值(如List)。类型值为Java编译器所用,确保用户代码类型安全。例如,编译器知道List的add()方法只接受Integer类型的参数,因此如果你在代码中将一个字符串传入add()将导致编译错误;编译器知道Iterator的next()方法返回一个Integer的实例,你在代码中也就无需对返回结果进行(Integer)强制转型。代码检验通过(语法正确且不会导致运行时类型安全问题)后,编译器对现有代码有一个转换工作。简单的说,就是去除代码中的类型值信息,在必要处添加转型代码。例如如下代码:public String demo() List strList = new ArrayList(); strList.add(“Hello!”); return strList.get(0);编译器在检验通过后,将其转换为:public String demo() List strList = new ArrayList(); /去除类型值 strList.add(“Hello!”); return (String)strList.get(0); /添加转型动作代码(String) 可见,类型值信息只为Java编译器在编译时所用,确保代码无类型安全问题;验证通过之后,即被去除。对于JVM而言,只有如JDK1.5之前版本一样的List,并无List和List之分。这也就是Java Generics实现中关键技术Erasure的基本思想。以下代码在控制台输出的就是“true”。List strList = new ArrayList();List intList = new ArrayList();System.out.println(strList.getClass() = intList.getClass(); 可以将Generic理解为:为提高Java代码类型安全性(在编译时确保,而非等到运行时才暴露),Java代码与Java编译器之间新增的一种约定规范。Java编译器在编译结果*.class文件中供JVM读取的部分里没有保留Generic的任何信息;JVM看不到Generic的存在。 对于Generic类(设为GenericClass)的类型参数(设为T):1) 由于对于JVM而言,只有一个GenericClass类,所以GenericClass类的静态字段和静态方法的定义中不能使用T。T只能出现在GenericClass的非静态字段或非静态方法中。也即T是与GenericClass的实例相关的信息;2) T只在编译时被编译器理解,因此也就不能与运行时被JVM理解并执行其代表的操作的操作符(如instanceof 和new)联用。class GenericClass T t1; public void method1(T t) t1 = new T(); /编译错误,T不能与new联用 if (t1 instanceof T) ; /编译错误,T不能与instanceof联用 ; static T t2; /编译错误,静态字段不能使用T public static void method2(T t);/编译错误,静态方法不能使用T Generic类可以有多个类型参数,且类型参数命名一般为大写单字符。例如Collection类库中的Map声明为:public interface Map ;3 Generic类和原(Raw)类对每一个Generic类,用户在使用时可以不指定类型参数。例如,对于List,用户可以以“List list;”方式使用,也可以以“List list;”方式使用。“List”被称为参数化的Generic类(类型参数被赋值),而“List”称为原类。原类List的使用方式和效果与JDK1.5之前版本List的一样;使用原类也就失去了Generic带来的可读性和健壮性的增强。 允许原类使用方式的存在显然是为了代码的向前兼容:即JDK1.5之前的代码在JDK1.5下仍然编译通过且正常运行。 当你在JDK1.5中使用原类并向原类实例中添加对象时,编译器会产生警告,因为它无法保证待添加对象类型的正确性。编译通过是为了保证代码向前兼容,产生警告是提醒潜在的风险。public void test () List list = new ArrayList(); list.add(tt);/JDK1.5编译器对此行产生警告4 Generic类和子类List ls = new ArrayList();List lo = ls; /编译错误:Type mismatch: cannot convert from List to /List以上第二行代码导致的编译错误“Type mismatch: cannot convert from List toList”是不是有点出人意料?直观上看,就像String是Object的子类,因此Object o = “String”合法一样,存放String的List是存放Object的List的子类,因此第二行应该是合法的。反过来分析,如果第二行是合法的,那么如下会导致运行时异常的代码也是合法的:lo.add(new Object); /会导致在ls中添加了非String对象String s = ls.get(0); /ls.get(0)返回的实际上只是一个Object实例,会导致ClassCastException 编译器显然不允许此种情形发生,因此不允许“List lo = ls”编译通过。 因此,直观上的“存放String的List是存放Object的List的子类”是错误的。更一般的说,设Foo是Bar的子类,G是Generic类型声明,G不是G的子类。5 参数化的Generic类和数组我们知道,如果T是S的子类,则T也是S的子类。因此,如下代码编译通过,只在运行时于第三行代码处抛ArrayStoreException。String words = new String10;Object objects = words;Objects0 = new Object(); /编译通过,但运行时会抛ArrayStoreException再分析如下代码:List wordLists = new ArrayList10;ArrayList integerList = new ArrayList();integerList.add(123);Object objects = wordLists;objects0 = integerList;/运行时不出错,因为运行时ArrayList和ArrayList都 /为ArrayListString s = wordlists0.get(0); /编译通过,运行时抛ClassCastException 就出现了“正确使用了Generic,但在运行时仍然出现ClassCastException”的情形。显然Java编译器不允许此种情形的发生。事实上,以上代码的第一行“List wordLists = new ArrayList10;”就是编译不通过的,也就不存在接下来的代码。更一般地说,不能创建参数化的Generic类的数组。6 类型参数通配符?由“Generic类和子类”节知,Collection不是存放其它类型对象的Collection(例如Collection)的基类(抽象),那么如何表示任一种参数化的Collection的呢?使用Collection。?即代表任一类型参数值。例如,我们可以很容易写出下面的通用函数printCollection():public static void printCollection(Collection c) /如此遍历Collection的简洁方式也是JDK1.5新引入的 for (Object o : c) System.out.println(o); 这样,既可以将Collection的实例,也可以将Collection的实例作为参数调用printCollection()方法。 然而,要注意一点,你不能往Collection容器实例中加入任何非null元素,例如如下代码的第三行编译不通过:public static void testAdd(Collection c) c.add(null); /编译通过 c.add(“test”); /编译错误 很好理解:c中要存放的对象的具体类型不确定,编译器无法验证待添加对象类型的正确性,因此不能加入对象实例;而null可以看作是任一个类的实例,因而允许加入。 另外,尽管c中要存放的对象的类型不确定,但我们知道任何类都是Object子类,因此从c中取出的对象都统一作为Object实例。 更进一步,如果BaseClass代表某个可被继承的类的类名,那么Collection代表类型参数值为BaseClass或BaseClass某个子类的任一参数化Collection。对于Collection的实例c,因为c中要存放的对象具体类型不确定,不能往其加入非null对象,但从c中取出的对象都统一作为BaseClass实例。事实上,你可以把Collection看作Collection的简洁形式。 另一种情形:如果SubClass代表任一个类的类名,那么Collection代表类型参数值为SubClass或SubClass某个祖先类的任一参数化Collection。对于Collection的实例c,你可以将SubClass实例加入其中,但从中取出的对象都是Object实例。7 Generic方法我们可以定义Generic类,同样可以定义Generic方法,即将方法的一个或多个参数的类型参数化,如代码:public static void fromArrayToCollection(T a, Collection c) for (T o : a) c.add(o); /合法。注意与Collection的区别 我们可以以如下方式调用fromArrayToCollection():Object oa = new Object100;Collection co = new ArrayList();fromArrayToCollection(oa, co); /此时,T即为ObjectString sa = new String100;Collection cs = new ArrayList();fromArrayToCollection(sa, cs); /此时,T即为StringfromArrayToCollection(sa, co); /此时,T即为ObjectInteger ia = new Integer100;Float fa = new Float100;Number na = new Number100;Collection cn = new ArrayList();fromArrayToCollection(
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025湖南湘潭市雨湖区招聘教师80人模拟试卷含答案详解
- 2025年潍坊职业学院高层次高技能人才引进(招聘)(10人)模拟试卷及一套完整答案详解
- 2025贵州黔南州瓮安县卫生健康局招聘公益性岗位人员2人模拟试卷带答案详解
- 2025广东广州市黄埔区老人院招聘5人考前自测高频考点模拟试题及答案详解(有一套)
- 2025广东阳江市阳春市统计局招聘编外人员1人模拟试卷附答案详解(典型题)
- 2025年四川省成都市青白江区七所“两自一包”公办学校招聘教师(152人)模拟试卷及完整答案详解1套
- 2025年合肥庐阳科技创新集团有限公司招聘6人考前自测高频考点模拟试题含答案详解
- 2025湖南邵阳公安局警务辅助人员招聘126人模拟试卷有完整答案详解
- 2025北京市第十九中学招聘考前自测高频考点模拟试题及答案详解(新)
- 2025湖北正源电力集团有限公司招聘146人(第三批)模拟试卷及答案详解一套
- 艾滋病的诊断和鉴别诊断
- 云鹤电商公司管理制度
- 新生儿臀部护理与纸尿裤使用指南
- 教育行业电销话术
- 2025-2030全球及中国汽车后桥转向系统行业市场现状供需分析及投资评估规划分析研究报告
- 租房安全协议合同书
- 智能书架解决方案
- 五金厂生产管理方案
- 江苏省南京师范大学附属中学2023-2024学年高二上学期期初测试数学试题
- (高清版)JTG 6310-2022 收费公路联网收费技术标准
- 快递实务(第2版)高职物流管理专业全套教学课件
评论
0/150
提交评论