java 泛型

一、概念

Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。

泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。

1. 泛型方法

具有一个或多个类型变量的方法,称之为泛型方法。类型变量也叫类型参数。

语法:

public static < E > void printArray( E[] e );
public static <T extends Comparable<T>> T maximum(T x, T y, T z);//有界(上届)的类型参数

 规则:

(1) 返回值之前类型变量声明;

(2) 多个类型参数声明时用逗号隔开;

(3) 泛型方法可以存在于非泛型类中;

(4) 类型参数能被用来声明返回值类型;

注意:类型参数只能代表引用型类型,不能是原始类型(像int,double,char的等)。

 要声明一个有界的类型参数,首先列出类型参数的名称,后跟extends关键字,最后紧跟它的上界。

 有界的类型参数:

要声明一个有界的类型参数,首先列出类型参数的名称,后跟extends关键字,最后紧跟它的上界。

 一个操作数字的方法可能只希望接受Number或者Number子类的实例. 

2. 泛型类

泛型类需要在类名后面添加了类型参数声明部分.

 语法:

public class Box<T>;

3. 类型通配符

(1) 类型通配符一般是使用?代替具体的类型参数。

语法:

public static void getData(List<?> data);
// 上届:通配符泛型只接受Number及其下层子类类型
public static void getUperNumber(List<? extends Number> data);

有界的类型参数:

<? extends T>表示该通配符所代表的类型是T类型的子类。 [上限]
<? super T>表示该通配符所代表的类型是T类型的父类。     [下限]

4. 常用通配符

常用以下字符表示参数类型:

E - Element (在集合中使用,因为集合中存放的是元素)

T - Type(Java 类)

K - Key(键)

V - Value(值)

N - Number(数值类型)

? - 表示不确定的java类型

S、U、V - 2nd、3rd、4th types

5. T和?的区别

T 是一个 确定的 类型,通常用于泛型类和泛型方法的定义,?是一个 不确定 的类型,通常用于泛型方法的调用代码和形参,不能用于定义类和泛型方法。

区别1:通过 T 来 确保 泛型参数的一致性

// 通过 T 来 确保 泛型参数的一致性
public <T extends Number> void
test(List<T> dest, List<T> src)

//通配符是 不确定的,所以这个方法不能保证两个 List 具有相同的元素类型
public void
test(List<? extends Number> dest, List<? extends Number> src)

区别2:类型参数可以多重限定而通配符不行
使用 & 符号设定多重边界(Multi Bounds),指定泛型类型 T 必须是 MultiLimitInterfaceA 和 MultiLimitInterfaceB 的共有子类型,此时变量 t 就具有了所有限定的方法和属性。对于通配符来说,因为它不是一个确定的类型,所以不能进行多重限定。


区别3:通配符可以使用超类限定而类型参数不行

// 类型参数 T 只具有 一种 类型限定方式:
T extends A

// 但是通配符 ? 可以进行 两种限定:
? extends A
? super A

6.Class<T> 和 Class<?> 区别

Class<T>和 Class<?> 最常见的是在反射场景下的使用。

// 通过反射的方式生成 multiLimit 对象,这里比较明显的是,我们需要使用强制类型转换
MultiLimit multiLimit = (MultiLimit)Class.forName("com.atguigu.MultiLimit").newInstance();

对于上述代码,在运行期,如果反射的类型不是 MultiLimit 类,那么一定会报 java.lang.ClassCastException 错误。

对于这种情况,则可以使用下面的代码来代替,使得在在编译期就能直接检查到类型的问题:

public class Test {
    public static <T> T getInstance(Class<T> cls) throws Exception {
        return cls.newInstance();
    }

    public static void main(String[] args) throws Exception {
        A a = getInstance(A.class);
        B b = getInstance(B.class);
    }
}

class A {}
class B {}

Class<T>在实例化的时候,T 要替换成具体类。Class<?>它是个通配泛型,? 可以代表任何类型,所以主要用于声明时的限制情况。比如,我们可以这样做申明:

public class Test2<T> {
    // 可以
    public Class<?> clazz;
    // 不可以,因为 T 需要指定类型
    public Class<T> clazzT;
}

所以当不知道定声明什么类型的 Class 的时候可以定义一 个Class。

那如果也想 public Class<T> clazzT;这样的话,就必须让当前的类也指定 T , 

public class Test2<T> {
    // 可以
    public Class<?> clazz;
    // 不可以,因为 T 需要指定类型
    public Class<T> clazzT;
}

二、使用教程

1. 泛型方法

// 泛型方法 printArray
public static <E> void printArray(E[] inputArray) {
    // 输出数组元素
    for (E element : inputArray) {
        System.out.printf("%s ", element);
    }
}

有界的泛型方法:

// 比较三个值并返回最大值  参数的类型必须实现了Comparable接口,即必须是可比较的
public static <T extends Comparable<T>> T maximum(T x, T y, T z) {
    T max = x; // 假设x是初始最大值
    if (y.compareTo(max) > 0) {
        max = y; //y 更大
    }
    if (z.compareTo(max) > 0) {
        max = z; // 现在 z 更大
    }
    return max; // 返回最大对象
}

2. 泛型类

public class Box<T> {
    private T t;

    public void add(T t) {
        this.t = t;
    }

    public T get() {
        return t;
    }

    public static void main(String[] args) {
        Box<String> stringBox = new Box<String>();
        stringBox.add(new String("陈数"));
        // 
是换行符,使光标定位到下一行
        System.out.printf("字符串为 :%s
", stringBox.get());
    }
}

3. 类型通配符

public static void getData(List<?> data) {
    System.out.println("data :" + data.get(0));
}

有上界的类型通配符:

public static void getUperNumber(List<? extends Number> data) {
    System.out.println("data :" + data.get(0));
}

 参考:

 java 泛型    

Java泛型中E、T、K、V等的含义

泛型通配符详解

原文地址:https://www.cnblogs.com/shiyun32/p/9717929.html