你的位置:首页 > Java教程

[Java教程]黑马程序员——泛型


------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------

(一).  泛型

1.1 介绍

  泛型是JDK5.0新增加的一个特性,泛型的本质是参数化类型,即所操作的数据类型都被指定为一个参数。这种类型参数可以用在类、接口、和方法的创建中,分别称为泛型类、泛型接口、泛型方法。Java语言引入泛型的好处是安全简单。

1.2 认识泛型

  在JDK5.0之前,没有泛型的情况下,通过对类型Object的引用来实现参数的"任意化",但"任意化"带来的缺点是需要显示的强制类型转换,此种转换要求开发者对实际参数类型预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不会提示错误,但在运行的时候会出现异常,这是一个安全隐患。

1.3 泛型的优势

  使用泛型的优势在于编译期间检查类型,捕捉类型不匹配错误,并且所有的转换都是自动和隐式多的,提高代码复用率。

(二).  泛型的使用 

2.1  泛型定义

  实例化泛型类的语法结构如下:

  1 classname<type-param-list> obj = new classname<type-param-list> (cons-arg-list);  

  泛型定义通常使用一个唯一的大写字母表示一个类型参数。

2.2  代码演示

 1 //创建泛型类 2 public class Generic <T> { 3   private T ob;//定义泛型成员变量 4   public Generic(T ob){ 5     this.ob = ob; 6   } 7   public T getOb(){ 8     return ob; 9   }10   public void setOb(T ob){11     this.ob = ob;12   }13   public void showType(){14     System.out.println("实际类型是:" + ob.getClass().getName());15   }16 }

  接下来创建类:

 1 //创建测试类,用于解释泛型的使用方法 2 public class GenericDemo { 3   public static void main(String[] args) { 4     //定义泛型类Genneric的一个Integer版本 5     Generic<Integer> intOb = new Generic<Integer>(88); 6     intOb.showType(); 7     int i = intOb.getOb(); 8     System.out.println("value=" + i); 9     System.out.println("---------------------------------");10     //定义泛型类Genneric的一个String版本11     Generic<String> strOb = new Generic<String>("Hello");12     strOb.showType();13     String s = strOb.getOb();14     System.out.println("value=" + s);15   }16 }

  运行结果:

1 实际类型是:java.lang.Integer2 value=883 ---------------------------------4 实际类型是:java.lang.String5 value=Hello

2.3  理解泛型需注意3点

  • 泛型的类型参数是类类型(包括自定义类),不能是基本数据类。
  • 同一种泛型可以对应多个版本(因为类型参数是不确定的),不同版本的泛型类实例是不兼容的。
  • 泛型的类型参数可以有多个。

(三).  有界类型

3.1  介绍

  在有些时候需要对类型参数的取值进行一定程度的限制,以使数据具有可操作性。为了处理这种情况,Java提供了有界类型。在指定类型参数时可以使用extends关键字限制此类型参数代表的类必须继承自指定父类或父类本身。比如创建一个类:public class BoundGeneric<T extends Number>{},BoundGeneric类的定义中,使用extends关键字将T的类型限制为Number类及其子类。

3.2  注意

  在使用extends(如:T extends someClass)声明的泛型类进行实例化时,运行传递的类型参数是:如果someClass是类,可以传递someClass本身及其子类,如果someClass是接口,则可以传递实现接口的类。

3.3  通配符

  通配符由”?“来表示,代表一个未知类型。

  例如:public static void func(Generic <?> T){}或者结合有界类型使用

     public static void func(Generic <? extends Number> T)

(四).  泛型的局限

4.1 泛型的局限性

  其实Java并没有真正的实现泛型,是编译器在编译的时候在字节码上了做手脚(成为擦除),这种实现理念造成java泛型本身有很多漏洞,局限性很大。其中大多数限制性是由类型擦除引起的。

  • 泛型不能被实例化。但可以通过调用Class.newInstance和Array.newInstance方法,利用反射构造泛型对象和数组。
  • 不能实例化泛型数组,即不能创建一个类型特定的泛型引用数组。如:Gen<String> []arrays = new Gen<String> [100];该语句是非法语句,因为会损害类型安全,但是如果使用通配符,就可以创建泛型类型的引用数组,如:Gen<?> []arrays = new Gen<?> [10];
  • 不能用类型参数替换基本类型。因为擦除类型后原先的类型参数被Object或者限定类型替换,而基本类型是不能被对象所存储的,但是可以使用基本类型的包装类来解决此问题。
  • 异常。不能抛出也不能捕获泛型类的异常对象,使用泛型类来扩展Throwable也是非法的。
1 public class GenericException <T> extends Exception{2   //泛型类无法继承Throwable,非法 3 }

  不能在catch子句中使用类型参数,如下面的方法将不能编译:

1 public static <T extends Throwable> void doWork(Class<T> t){2   try {3   } catch (T e) {//不能捕获类型参数异常4   }5 }

  但是,在异常声明中可以使用类型参数。下面这个是合法的:

1 public static <T extends Throwable> void doWork(T t) throws T {2   try {3   } catch (Throwable realCause) {//不能捕获类型参数异常4     throw t;    5   }6 }

  • 静态成员。不能在静态变量或者静态方法中引用类型参数。如下述语句是非法的:
1 public class Gen<T>{2   //静态变量不能引用类型参数3   static T ob;4   //静态方法不能引用类型参数5   static T getOb(){6     return ob;7   }8 }

  尽管不能在静态变量或静态方法中引用类型参数,但可以声明静态泛型方法。