泛型

(一).  泛型

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.Integer
2 value=88
3 ---------------------------------
4 实际类型是:java.lang.String
5 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 }

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

(五).  技巧

  当方法静态时,不能访问类上定义的泛型,如果静态方法使用泛型,只能将泛型定义在方法上,注意放置位置:public static <Y> void method(Y obj)

  ? extends E :接收E类型或者E的子类对象((对于本身来说是)上限)

  ? super E  :接收E类型或者E的父类型(下限)

  在集合存元素时,一般使用上限,因为这样取出都是按照上限类型来运算,不会出现安全隐患。

  什么时候使用下限呢?

  通常对集合中的元素进行取出操作时,可以用下限。

原文地址:https://www.cnblogs.com/pingfan21/p/4940668.html