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的元素也没问题。

原文地址:https://www.cnblogs.com/aland-1415/p/14367699.html