最后更新于 2022 年 5 月 24 日

Featured image of post 泛型

泛型

泛型的简单面试题

1. Java中的泛型是什么 ? 使用泛型的好处是什么? 原则是什么?

Java泛型设计原则:只要在编译时期没有出现警告,那么运行时期就不会出现ClassCastException异常。

Java泛型设计原则:只要在编译时期没有出现警告,那么运行时期就不会出现ClassCastException异常。

泛型,即“参数化类型”,把类型明确的工作推迟到创建对象或调用方法的时候才去明确的特殊类型,把<数据类型>当作是参数一样传递

好处,在集合中存储对象并在使用前进行类型转换是多么的不方便。泛型防止了那种情况的发生。它提供了编译期的类型安全,确保你只能把正确类型的对象放入集合中,避免了在运行时出现ClassCastException。


2. Java的泛型是如何工作的 ? 什么是类型擦除 ?

  泛型是通过类型擦除来实现的,编译器在编译时擦除了所有类型相关的信息,所以在运行时不存在任何类型相关的信息。例如List在运行时仅用一个List来表示。这样做的目的,是确保能和Java 5之前的版本开发二进制类库进行兼容。你无法在运行时访问到类型参数,因为编译器已经把泛型类型转换成了原始类型。

 //类型被擦除了,保留的是类型的上限,String的上限就是Object
        List list = strlist;

        List<String> stringList2 = list;
        List<Integer> intList2 = list;
        //你也可以把它赋给Integer类型的集合,但是当你把这个集合当
        //成Integer的集合操作的时候,依旧会抛出ClassCastException异常

        for (Integer i:intList2){//java.lang.ClassCastException
            System.out.println(i);
        }

3. 什么是泛型中的限定通配符和非限定通配符 ?

限定通配符对类型进行了限制。有两种限定通配符,一种是<? extends T>它通过确保类型必须是T的子类来设定类型的上界,另一种是<? super T>它通过确保类型必须是T的父类来设定类型的下界。泛型类型必须用限定内的类型来进行初始化,否则会导致编译错误。另一方面表示了非限定通配符,因为可以用任意类型来替代。

public class Animal {
    public static void main(String[] args) {
        ArrayList<Animal> animals=new ArrayList<>();
        ArrayList<Cat> cats=new ArrayList<>();
        ArrayList<? extends Cat> miniCats=new ArrayList<>();

        showAnimal(cats);
        showAnimal(miniCats);
        //showAnimal(animals);
    }

    //类型通配符的上限,要求该泛型的类型,只能是实参类型,或实参类型的子类类型。
    //此处上限是Cat,包括MiniCat但是不包括Anmial的
    public static void showAnimal(ArrayList <? extends Cat> list){
        for (int i = 0; i < list.size(); i++) {
            Cat cat = list.get(i);
            System.out.println(cat);
        }
    }

    //要求该泛型的类型,只能是实参类型,或实参类型的父类类型。
    //类型通配符的下限,要求只能是Cat或者是Cat的父类
    public static void showAnimal2(List<? super Cat> list){
        //增强for循环,用object类型的o接受,遍历list
        for (Object o : list) {
            System.out.println(o);
        }
    }

}
class Cat extends Animal{

}

class MiniCat extends Animal{
}

4.可以把List< String>传递给一个接受List < Object>参数的方法吗?

  对任何一个不太熟悉泛型的人来说,这个Java泛型题目看起来令人疑惑,因为乍看起来String是一种Object,所以List< String>应当可以用在需要List< Object>的地方,但是事实并非如此。真这样做的话会导致编译错误。如果你再深一步考虑,你会发现Java这样做是有意义的,因为List< Object>可以存储任何类型的对象包括String, Integer等等,而List< String>却只能用来存储Strings。

例一:

       //这里与
       List<Object> objectList;
       List<String> stringList;
       objectList = stringList;  
       //compilation error incompatible types

例二:

 List list = new ArrayList();//默认类型是Object
        list.add("A123");
        list.add("B234");
        list.add("C345");
        System.out.println(list);

        for(int i=0;i<list.size();i++){
            //若要将list中的元素赋给String变量,需要进行类型
            //转换compatible types错误,显示list.get(i)返回的是Object
            String str =  (String) list.get(i);
            System.out.println(str);
        }
   
        list.add(123);
        //因为类型是Object,我们可以把Integer类型或者其他数据类型的元素也加入list之中
        System.out.println(list.get(3));

        for(int i=0;i<list.size();i++){
            //String str =  (String) list.get(i);
            //但是在这里会报错java.lang.ClassCastException,
            //我们不能直接将Integer类型的数据转换成String
            System.out.println(list.get(i).getClass());
        }

如代码中所示,当我们定义了一个List,list默认的类型是所有对象的基类Object,那么我们取出数据的时候需要经过一次类型转换才能进行对象的实际类型的相关操作。因为List中的类型是Object,那么我们先添加了String类型的数据,然后再添加Integer或者其他类型的数据也是允许的,因为编译时List中是Object类型的数据,然而运行的时候却是它本身的类型,所以当我们将List中的数据当作String处理时会抛出java.lang.ClassCastException


5. Array中可以用泛型吗?

  这可能是Java泛型面试题中最简单的一个了,当然前提是你要知道Array事实上并不支持泛型,这也是为什么Joshua Bloch在Effective Java一书中建议使用List来代替Array,因为List可以提供编译期的类型安全保证,而Array却不能。

本文参考:

https://www.jb51.net/article/214509.htm

https://cloud.tencent.com/developer/article/1033693

Built with Hugo