Java 泛型是从 jdk1.5 开始加入的,Java 泛型的实现采取了 “伪泛型” 的策略,即 Java 在语法上支持泛型,但是在编译阶段会进行所谓的 “类型擦除”,将所有的泛型表示 (尖括号中的内容) 都替换为具体的类型 (其对应的原生态类型),就像完全没有泛型一样。

引入泛型的意义在于:

  • 适用于多种数据类型执行相同的代码 (代码复用)
private static int add(int a, int b) {
    System.out.println(a + "+" + b + "=" + (a + b));
    return a + b;
}

private static float add(float a, float b) {
    System.out.println(a + "+" + b + "=" + (a + b));
    return a + b;
}

private static double add(double a, double b) {
    System.out.println(a + "+" + b + "=" + (a + b));
    return a + b;
}

如果没有泛型,要实现不同类型的方法,每种类型都需要重载一个 add 方法,通过泛型,我们可以复用为一个方法:

private static <T extends Number> double add(T a, T b) {
    System.out.println(a + "+" + b + "=" + (a.doubleValue() + b.doubleValue()));
    return a.doubleValue() + b.doubleValue();
}
  • 泛型中的类型在使用时指定,不需要强制类型转换 (类型安全,编译器会检查类型)

# 泛型的基本使用

#

  • 从一个简单的泛型类看起:
class Point<T>{         // 此处可以随便写标识符号,T是type的简称  
    private T var ;     // var的类型由T指定,即:由外部指定  
    public T getVar(){  // 返回值的类型由外部决定  
        return var ;  
    }  
    public void setVar(T var){  // 设置的类型也由外部决定  
        this.var = var ;  
    }  
}  
public class GenericsDemo06{  
    public static void main(String args[]){  
        Point<String> p = new Point<String>() ;     // 里面的var类型为String类型  
        p.setVar("it") ;                            // 设置字符串  
        System.out.println(p.getVar().length()) ;   // 取得字符串的长度  
    }  
}
  • 多元泛型
class Notepad<K,V>{       // 此处指定了两个泛型类型  
    private K key ;     // 此变量的类型由外部决定  
    private V value ;   // 此变量的类型由外部决定  
    public K getKey(){  
        return this.key ;  
    }  
    public V getValue(){  
        return this.value ;  
    }  
    public void setKey(K key){  
        this.key = key ;  
    }  
    public void setValue(V value){  
        this.value = value ;  
    }  
} 
public class GenericsDemo09{  
    public static void main(String args[]){  
        Notepad<String,Integer> t = null ;        // 定义两个泛型类型的对象  
        t = new Notepad<String,Integer>() ;       // 里面的key为String,value为Integer  
        t.setKey("汤姆") ;        // 设置第一个内容  
        t.setValue(20) ;            // 设置第二个内容  
        System.out.print("姓名;" + t.getKey()) ;      // 取得信息  
        System.out.print(",年龄;" + t.getValue()) ;       // 取得信息  
    }  
}

# 泛型接口

  • 简单的泛型接口
interface Info<T>{        // 在接口上定义泛型  
    public T getVar() ; // 定义抽象方法,抽象方法的返回值就是泛型类型  
}  
class InfoImpl<T> implements Info<T>{   // 定义泛型接口的子类  
    private T var ;             // 定义属性  
    public InfoImpl(T var){     // 通过构造方法设置属性内容  
        this.setVar(var) ;    
    }  
    public void setVar(T var){  
        this.var = var ;  
    }  
    public T getVar(){  
        return this.var ;  
    }  
} 
public class GenericsDemo24{  
    public static void main(String arsg[]){  
        Info<String> i = null;        // 声明接口对象  
        i = new InfoImpl<String>("汤姆") ;  // 通过子类实例化对象  
        System.out.println("内容:" + i.getVar()) ;  
    }  
}

# 泛型方法

泛型方法,是在调用方法的时候指明泛型的具体类型。

  • 定义泛型方法的语法格式
public class Sace {

    // <T> 声明此方法持有一个类型T,也可以理解为声明此方法为泛型方法
    // T 指明该方法返回对象为T类型
    // Class<T> 指明泛型的具体类型
    public <T> T getObject(Class<T> c) throws InstantiationException, IllegalAccessException {
        T t = c.newInstance();
        return t;
    }

    public static void main(String[] args) throws InstantiationException, IllegalAccessException {
        Sace sace = new Sace();
        Class<Date> aClass = Date.class;
        Date object = sace.getObject(aClass);
        System.out.println(object);
    }
}

# 泛型的上下限

以下代码会报错

class A{}
class B extends A {}

// 如下两个方法不会报错
public static void funA(A a) {
    // ...          
}
public static void funB(B b) {
    funA(b);
    // ...             
}

// 如下funD方法会报错
public static void funC(List<A> listA) {
    // ...          
}
public static void funD(List<B> listB) {
    funC(listB); // Unresolved compilation problem: The method doPrint(List<A>) in the type test is not applicable for the arguments (List<B>)
    // ...             
}

为了解决泛型中隐含的转换问题,Java 泛型加入了类型参数的上下边界机制。<? extends A> 表示该类型参数可以是 A (上边界) 或者 A 的子类类型。编译时擦除到类型 A,即用 A 类型代替类型参数。这种方法可以解决开始遇到的问题。

public static void funC(List<? extends A> listA) {
    // ...          
}
public static void funD(List<B> listB) {
    funC(listB); // OK
    // ...             
}
  • 泛型上下限的引入
    在使用泛型的时候,我们可以为传入的泛型类型实参进行上下边界的限制,比如:类型实参只准传入某种类型的夫类型或某种类型的子类

上限

public class Info<T extends Number> {
    T val;

    public T getVal() {
        return val;
    }

    public void setVal(T val) {
        this.val = val;
    }

    public static void main(String[] args) {
        Info info = new Info<Double>();
        Info info2 = new Info<Integer>();
        info.val = 10;
        info2.val = 10f;
        System.out.println(info.val);
        System.out.println(info2.val);
    }
}

下限

public class Info<T> {
    T val;

    public T getVal() {
        return val;
    }

    public void setVal(T val) {
        this.val = val;
    }

    public static void fun(Info<? super String> temp) {    // 只能接收String或Object类型的泛型,String类的父类只有Object类
        System.out.print(temp + ", ");
    }

    public static void main(String[] args) {
        Info<String> info = new Info<String>();
        Info<Object> info2 = new Info<Object>();
        Info<Integer> info3 = new Info<Integer>();
        fun(info);
        fun(info2);
        fun(info3); // info3不允许作为fun的形参,info3是Integer泛型对象,需要一个String的父类,包括String类
    }
}

总结:

  1. 无限制通配符
  2. extends 关键字声明了类型的上界,表示参数化的类型可能是所指定的类型,或者是此类型的子类
  3. super 关键字声明了类型的下界,表示了参数化的类型可能是指定类型,或者是次类型的父类