zoukankan      html  css  js  c++  java
  • Java泛型中的通配符T,E,K,V

    来源:微信公众号:[Java基基]

    Java泛型提供了编译时类型安全监测机制,该机制允许开发者在编译时检测到非法的类型。

    1.泛型的好处

    在没有泛型的情况的下,通过对类型 Object 的引用来实现参数的“任意化”,“任意化”带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是本身就是一个安全隐患。

    泛型的好处就是在编译的时候能够检查类型安全,并且所有的强制转换都是自动和隐式的。

    2.泛型中的通配符

    T,E,K,V

    2.1 T,E,K,V,?

    通常情况下,T,E,K,V,?是这样约定的:

    1. ?表示不确定的Java类型
    2. T(type)表示具体的一个Java类型
    3. KV(key value)分别代表Java键值中的key value
    4. E(element)代表Element

    2.2 ?无界通配符

    对于不确定或者不关心实际要操作的类型,可以使用无限制通配符(即尖括号里一个问号),表示可以持有任何类型。

    2.3 上界通配符 < ? extends E>

    用 extends 关键字声明,表示参数化的类型可能是所指定的类型,或者是此类型的子类。
    

    在类型参数中使用 extends 表示这个泛型中的参数必须是 E 或者 E 的子类,这样有两个好处:

    • 如果传入的类型不是 E 或者 E 的子类,编译不成功
    • 泛型中可以使用 E 的方法,要不然还得强转成 E 才能使用
    private <K extends A, E extends B> E test(K arg1, E arg2){
        E result = arg2;
        arg2.compareTo(arg1);
        //.....
        return result;
    }
    

    类型参数列表中如果有多个类型参数上限,用逗号分开。

    2.4 下界通配符 < ? super E>

    用 super 进行声明,表示参数化的类型可能是所指定的类型,或者是此类型的父类型,直至 Object。

    在类型参数中使用 super 表示这个泛型中的参数必须是 E 或者 E 的父类。

    private <T> void test(List<? super T> dst, List<T> src){
        for (T t : src) {
            dst.add(t);
        }
    }
    
    public static void main(String[] args) {
        List<Dog> dogs = new ArrayList<>();
        List<Animal> animals = new ArrayList<>();
        new Test3().test(animals,dogs);
    }
    // Dog 是 Animal 的子类
    class Dog extends Animal {
    }
    

    2.5 ?和 T 的区别

    在这里插入图片描述
    ?和 T 都表示不确定的类型,区别在于我们可以对 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:类型参数可以多重限定而通配符不行

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

    3.ClassClass区别

    Class和 <Class<?>又有什么区别呢?

    在反射场景下的使用:

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

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

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

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

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

    当不知道定声明什么类型的 Class 的时候可以定义一 个Class。
    在这里插入图片描述

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

    public class Test3<T> {
        public Class<?> clazz;
        // 不会报错
        public Class<T> clazzT;
    
  • 相关阅读:
    Good Bye 2014 B. New Year Permutation(floyd )
    hdu 5147 Sequence II (树状数组 求逆序数)
    POJ 1696 Space Ant (极角排序)
    POJ 2398 Toy Storage (叉积判断点和线段的关系)
    hdu 2897 邂逅明下 (简单巴什博弈)
    poj 1410 Intersection (判断线段与矩形相交 判线段相交)
    HDU 3400 Line belt (三分嵌套)
    Codeforces Round #279 (Div. 2) C. Hacking Cypher (大数取余)
    Codeforces Round #179 (Div. 2) B. Yaroslav and Two Strings (容斥原理)
    hdu 1576 A/B (求逆元)
  • 原文地址:https://www.cnblogs.com/aixing/p/13327345.html
Copyright © 2011-2022 走看看