zoukankan      html  css  js  c++  java
  • Java泛型


        从Java SE5开始有了泛型的概念,泛型实现了参数化类型的概念。泛型的核心概念就是告诉编译器想使用什么类型,一般可以认为泛型与其他类型差不多,只是带有一个类型参数罢了。泛型的类型参数可以有多个,以逗号分隔,同时也可以使用extends来限定类型,extends为边界符,与继承时使用的extends意义不同,多个边界使用&符号连接。

    1、简单的泛型

    public class ClassHolder<T> {
        public  List<T> getClassList(){
            return new ArrayList<T>();
        }
    
        public static void main(String[] args) {
            //如下调用getClassList则会自动生成一个String的List
            List<String> list = new ClassHolder<String>().getClassList();
        }
    }

    接口也可以使用泛型,方法和类的一样

    public interface test<T>{}
    public interface test<T,E>{}
    public interface test<T extends E>{}
    public interface test<T extends E&K>{}

    方法使用泛型,只需要将泛型参数列表置于返回值之前,即可;

    public <E> void test(){}

    2、类型参数推断

    在进行赋值操作时,编译器可根据结果类型自动推断出泛型方法所需要的类型;

    public class ClassHolder {
        public static <E> List<E> getClassList(){
            return new ArrayList<E>();
        }
    
        public static void main(String[] args) {
            //如下调用getClassList可不传类型参数,编译器可以根据stringList的类型,自动帮忙推断出getClassList()需要填充的类型是String
            List<String> stringList = ClassHolder.getClassList();
            //当然也可以如下这样显示的指出类型参数。不显示的指出虽然省了代码量,但有时候对代码的阅读难度提高了。
            List<String> stringList2 = ClassHolder.<String>getClassList();
            //如下这般非赋值操作的调用则不会进行类型推断,最终返回值的类型参数是Object
            ClassHolder.getClassList();
        }
    }

    3、通配符?的使用

    主要在实例时的一些使用;
    例:

    public class StudyTest {}
    public class StudyTestChild1 extends StudyTest { }
    public class StudyTestChild2 extends StudyTest { }
    public class MainClass {
        public static void main(String[] args) {
    
            //1、边界通配符: <? extends T>,只允许单一边界,即只能extends一个类型
            // 不允许创建
            //List<StudyTest> list = new ArrayList<StudyTestChild>();
            // 允许创建, list是具有任何从StudyTest继承类型的列表, 这样就将将两种类型建立了联系
            List<? extends StudyTest> list = new ArrayList<StudyTestChild1>();
            //不能向list里添加任何类型
    //        list.add(new StudyTestChild1());
    //        list.add(new StudyTest());
            //但取出来的类型可以直接就是边界类型
            StudyTest s = list.get(0);
    
            //2、超类型通配符 : <? super T>
            List<? super StudyTest> list1 = new ArrayList<>();
            //可以add,这里好像可以直接使用StudyTest来限制list1的类型,加上? super StudyTest这样有什么区别。。
            list1.add(new StudyTestChild1());
    
            //3、无边界通配符:<?> ,即任意类型(但只能是一种类型),多数情况下似乎和Object的效果一样,只是使用了泛型而已。
            //当处理多个泛型参数时,有时允许一个参数可以是任何类型,同时为其他参数确实某种特定类型。
            Map<String, ?> map2 = new HashMap<String, Integer>();
    
        }
    }

    4、擦除机制(重要)

    1)在泛型代码内部,无法获取任何有关泛型参数类型的信息,如:List<String> list = new ArrayList<String>(),即使list已经实例,但通过list.getClass().getTypeParameters()也不能获取到String这个信息,只能获取到一个泛型E的标识符。这也就是java泛型的擦除机制,即在使用泛型时,任何具体的类型信息都被擦除了,唯一知道的就是你在使用一个对象,在运行时List<String>和List<Integer>的类型是一样的。
    2)因为泛型不是java一开始就有的,擦除机制也是因java语言的向前兼容造成的折中方案。
    3)泛型类型只有在静态类型检查期间才会出现,在此之后,程序中的所有泛型类型都将被擦除,替换为它们的非泛型上界。诸如List<T>这样的会被擦除为List,普通的类型变量会被擦除为Object,如果是T extends String这样的带有extends的会被擦除为String。
    4)对于已擦除了类型信息的不能使用instanceof来比较两个类型是否相同(因为都是Object),但可以使用动态的isInstance()方法来判断。
    5)想要实例一个传入参数类型的类,可使用Class的newInstance()来实现。直接实例不行,因为类型已经被擦除了。如不能使用new T()来实例,但可以使用new Class<T>().newInstance()实例化一个T类型实例。但这种方法对于没有默认构造器的类(如Integer)来说在运行时会报错,这个需要注意。

    5、其他

    1)泛型也可以应用于内部类、匿名内部类;
    2)泛型使用原则:只要能够使用,就应该尽量使用泛型;
    3)static方法无法访问泛型类的类型参数
    4)泛型的出现的原因
    有许多原因促成了泛型的出现,最初最大的原动力就是为了创建容器类(List,Map,Set等)。
    Java的泛型相对于其他实现了更纯粹泛型的语言(如C++)来说有许多限制。
    如:(1)基本类型无法作为泛型的参数
    (2)一个类不能同时实现同一个泛型接口的两种变体,因为擦除原因,这两个变体会被认为是相同的接口;
    (3)使用带有泛型类型参数的转型,没有意义,使用instanceof也只会得到相同的结果
    (4)不能使用泛型重载方法
    如下是不行的:
    public class ClassHolder<T, E> {
    void get(T t){};
    void get(E e){};
    }
    (5)自限定类型:class ClassHolder<T extends ClassHolder<T>> {} (这个很好的说明了extends 在此处的功能不是继承,而只是边界的作用。)
    (6)泛型与数组不能较好的使用,因为数组必须知道它所持有的确切类型。如 List<String>[ ] 这样的数组实际是一个Object[ ],所以向里边添加Interger类型List的元素也没问题。

  • 相关阅读:
    区块链|学习笔记(三)
    左神算法之获取栈中最小值
    23种设计模式之适配器模式
    二叉树序列化和反序列化
    归并排序
    通过集合构建RDD或者DataFrame
    内核源码分析——shuffle
    问题
    函数参数
    问题记录
  • 原文地址:https://www.cnblogs.com/aland-1415/p/14367699.html
Copyright © 2011-2022 走看看