`
lud35lud
  • 浏览: 19291 次
最近访客 更多访客>>
社区版块
存档分类
最新评论

Java5新特性

 
阅读更多

Java5新特性
2011年06月01日
  (一)泛型
  1.泛型的本质是参数化类型,通俗的讲就是创建一个用类型作为参数的类:
  规则:a、泛型的类型参数只能是类类型(包括自定义类),不能是简单类型。
  b、同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的。
  c、泛型的类型参数可以有多个。
  d、泛型的参数类型可以使用extends语句,例如。习惯上称为“有界类型”。
  e、泛型的参数类型还可以是通配符类型。例如Class classType = Class.forName(java.lang.String); List list = new ArrayList(); //泛型 list.add(new Integer(10)); Integer a = list.get(0); Map m = new HashMap(); m.put("a",1); //自动封装机制 m.put("b",2); m.put("c",3); int b = m.get("b"); //自动解封机制
  2.自定义泛型类 public class Gen { private T ob; //定义泛型成员变量 public Gen(T ob) { this.ob = ob; } public T getOb() { return ob; } public void setOb(T ob) { this.ob = ob; } public void showTyep() { System.out.println("T的实际类型是: " + ob.getClass().getName()); } } public class GenDemo { public static void main(String[] args){ //定义泛型类Gen的一个Integer版本 Gen intOb=new Gen(88); intOb.showTyep(); int i= intOb.getOb(); System.out.println("value= " + i); System.out.println("------------------------------ ----"); //定义泛型类Gen的一个String版本 Gen strOb=new Gen("Hello Gen!"); strOb.showTyep(); String s=strOb.getOb(); System.out.println("value= " + s); } } 
  在Java 5之前,为了让类有通用性,往往将参数类型、返回类型设置为Object类型,当获取这些返回类型来使用时候,必须将其“强制”转换为原有的类型或者接口,然后才可以调用对象上的方法。
  强制类型转换很麻烦,我还要事先知道各个Object具体类型是什么,才能做出正确转换。否则,要是转换的类型不对,比如将“Hello Generics!”字符串强制转换为Double,那么编译的时候不会报错,可是运行的时候就挂了。那有没有不强制转换的办法----有,改用 Java5泛型来实现。
  3.用extends和super来控制泛型的类型范围
  class A { };
  表示B只能是List的子类或子接口A a= new A();就是错误的。
  如果用super的话就是表示父类或者父接口了。
  4.通配符 void print(List a) { for (Integer i : a) { //表示从a中不断的取出对象定义成 i 然后进行循环 System.out.println(i); } } void print(List a) { // 类型通配符,用?表示不知道List里放的什么类型 for (Integer i : a) { System.out.println(i); } } 
  看一个有趣的例子: public class CollectionGenFooDemo { public static void main(String args[]) { CollectionGenFoo listFoo = null; listFoo = new CollectionGenFoo(new ArrayList()); //出错了,不让这么干。 // CollectionGenFoo listFoo = null; // listFoo=new CollectionGenFoo(new ArrayList()); System.out.println("实例化成功!"); } } 
  当前看到的这个写法是可以编译通过,并运行成功。可是注释掉的两行加上就出错了,因为这么定义类型的时候,就限定了构造此类实例的时候T是确定的一个类型,这个类型实现了Collection接口,但是实现 Collection接口的类很多很多,如果针对每一种都要写出具体的子类类型,那也太麻烦了,我干脆还不如用Object通用一下。别急,泛型针对这种情况还有更好的解决方案,那就是“通配符泛型”。
  为了解决类型被限制死了不能动态根据实例来确定的缺点,引入了“通配符泛型”,针对上面的例子,使用通配泛型格式为,“?”代表未知类型,这个类型是实现Collection接口。那么上面实现的方式可以写为: public class CollectionGenFooDemo { public static void main(String args[]) { CollectionGenFoo listFoo = null; listFoo = new CollectionGenFoo(new ArrayList()); //现在不会出错了 CollectionGenFoo listFoo1 = null; listFoo=new CollectionGenFoo(new ArrayList()); System.out.println("实例化成功!"); } }
  注意:
  1)、如果只指定了,而没有extends,则默认是允许Object及其下的任何Java类了。也就是任意类。
  2)、通配符泛型不单可以向下限制,如,还可以向上限制,如,表示类型只能接受Double及其上层父类类型,如Number、Object类型的实例。
  3)、泛型类定义可以有多个泛型参数,中间用逗号隔开,还可以定义泛型接口,泛型方法。这些都泛型类中泛型的使用规则类似。
  (二)增强的for循环 void print(List a) { for (Integer i : a) { System.out.println(i); } }
  (三)可变参数(Vararg)
  实现可变参数,传统上一般是采用“利用一个数组来包裹要传递的实参”的做法来应付.
  J2SE 1.5中提供了Varargs机制,允许直接定义能和多个实参相匹配的形参。从而,可以用一种更简单的方式,来传递个数可变的实参。
  1.定义实参个数可变的方法
  在一个形参的“类型”与“参数名”之间加上三个连续的“.” private static int sumUp(int... values) { }
  注意,只有最后一个形参才能被定义成“能和不确定个实参相匹配”的。因此,一个方法里只能有一个这样的形参。另外,如果这个方法还有其它的形参,要把它们放到前面的位置上。
  编译器会在背地里把这最后一个形参转化为一个数组形参,并在编译出的class文件里作上一个记号,表明这是个实参个数可变的方法。
  实参个数可变的方法的秘密形态 private static int sumUp(int[] values) { }
  由于存在着这样的转化,所以不能再为这个类定义一个和转化后的方法签名一致的方法。
  会导致编译错误的组合 private static int sumUp(int... values) { } private static int sumUp(int[] values) { }
  2.调用实参个数可变的方法
  可以传递若干个实参
  sumUp(1, 3, 5, 7); 
  在背地里,编译器会把这种调用过程转化为用“数组包裹实参”的形式:
  偷偷出现的数组创建
  sumUp(new int[]{1, 2, 3, 4});
  另外,这里说的“不确定个”也包括零个,所以这样的调用也是合乎情理的:也可以传递零个实参
  sumUp();
  这种调用方法被编译器秘密转化之后的效果,则等同于这样:零实参对应空数组
  sumUp(new int[]{});
  注意这时传递过去的是一个空数组,而不是null。这样就可以采取统一的形式来处理,而不必检测到底属于哪种情况。
  3.是数组?不是数组?
  尽管在背地里,编译器会把能匹配不确定个实参的形参,转化为数组形参;而且也可以用数组包了实参,再传递给实参个数可变的方法;但是,这并不表示“能匹配不确定个实参的形参”和“数组形参”完全没有差异。
  一个明显的差异是,如果按照调用实参个数可变的方法的形式,来调用一个最后一个形参是数组形参的方法,只会导致一个“cannot be applied to”的编译错误。 private static void testOverloading(int[] i) { System.out.println("A"); } public static void main(String[] args) { testOverloading(1, 2, 3);//编译出错 }
  由于这一原因,不能在调用只支持用数组包裹实参的方法的时候(例如在不是专门为J2SE 1.5设计第三方类库中遗留的那些),直接采用这种简明的调用方式。
  如果不能修改原来的类,为要调用的方法增加参数个数可变的版本,而又想采用这种简明的调用方式,那么可以借助“引入外加函数(Introduce Foreign Method)”和“引入本地扩展(Intoduce Local Extension)”的重构手法来近似的达到目的。
  4.当个数可变的实参遇到泛型
  J2SE 1.5中新增了“泛型”的机制,可以在一定条件下把一个类型参数化。例如,可以在编写一个类的时候,把一个方法的形参的类型用一个标识符(如T)来代表,至于这个标识符到底表示什么类型,则在生成这个类的实例的时候再行指定。这一机制可以用来提供更充分的代码重用和更严格的编译时类型检查。
  不过泛型机制却不能和个数可变的形参配合使用。如果把一个能和不确定个实参相匹配的形参的类型,用一个标识符来代表,那么编译器会给出一个“generic array creation”的错误。
  当Varargs遇上泛型: private static  void testVarargs(T... args) {//编译出错 }
  造成这个现象的原因在于J2SE 1.5中的泛型机制的一个内在约束――不能拿用标识符来代表的类型来创建这一类型的实例。在出现支持没有了这个约束的Java版本之前,对于这个问题,基本没有太好的解决办法。
  不过,传统的“用数组包裹”的做法,并不受这个约束的限制。
  可以编译的变通做法 private static  void testVarargs(T[] args) { for (int i = 0; i
  
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics