zoukankan      html  css  js  c++  java
  • 泛型

    (一).  泛型

    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的父类型(下限)

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

      什么时候使用下限呢?

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

  • 相关阅读:
    C语言开发框架、printf(day02)
    Linux C(day01)
    线程同步、信号量、system v IPC
    UDP、线程、mutex锁(day15)
    Socket编程(day14)
    共享内存、网络(day13)
    pause、jobs、setitimer(2)、system v ipc(day12)
    PIPE、SIGNAL(day11)
    环境变量、system(day10)
    进程(day09)
  • 原文地址:https://www.cnblogs.com/pingfan21/p/4940668.html
Copyright © 2011-2022 走看看