彻底理解java中泛型

一、什么是泛型?

  泛型是JDK5引入的一种特性,是一种类型安全检测机制,开发者在编译阶段发现类型相关的报错。

  泛型即参数类型化,将操作的数据类型定义为参数,可定义在类、接口、方法中。

  可以把类型参数看作是使用参数化类型时指定的类型的一个占位符,就像方法的形式参数是运行时传递的值的占位符一样。

二、为什么有泛型?

  我们都知道java中泛型又称为“伪泛型”,为什么称为伪泛型呢?是因为泛型只存在编译阶段,编译完成之后泛型会被擦除,并没有作用在程序的执行阶段,所以他存在的目的就是“为了规范”。

1、类型安全

  泛型的主要目标是提高 Java 程序的类型安全,保证程序的可读性和安全性。

2、消除强制类型转换

  泛型的一个附带好处是,消除源代码中的许多强制类型转换。这使得代码更加可读,并且减少了出错机会。

3、潜在的性能收益

  泛型为较大的优化带来可能。在泛型的初始实现中,编译器将强制类型转换(没有泛型的话,程序员会指定这些强制类型转换)插入生成的字节码中。但是更多类型信息可用于编译器这一事实,为未来版本的 JVM 的优化带来可能。

三、泛型的使用

1、泛型类、泛型接口、泛型方法

public class Test {

    public T test2(T params){}
}

public interface Test1{}

2、泛型通配符

  比如常见的?,T,K,V 就是通配符,比如其中的T换成A-Z其中任何一个都可以,java中是讲究约定的,就是大家共同约定,某个字符代表什么意思,这样也有利于代码维护。

  • ? 表示不确定的 java 类型
  • T (type) 表示具体的一个java类型
  • K V (key value) 分别代表java键值中的Key Value
  • E (element) 代表Element

3、上界通配符(

  通配符上界,可以限制传入的类型必须是上界这个类或者是这个类的子类

 
//可以传入的实参类型是Number或者Number的子类

彻底理解java中泛型_第1张图片

public class Test {
    public static void printList1(List list) {
        for (Object x:list) {
            System.out.println(x);
        }
    }
    public static void main(String[] args) {
        List list = new ArrayList<>();
        list.add(1);
        printList1(list);  // ok
        List list1 = new ArrayList<>();
        list1.add(1.0D);
        printList1(list1);  // ok
        List list2 = new ArrayList<>();
        list2.add("1");
        printList1(list2); // compile error
        List list3 = list;
        // get能用上界
        Number o = list3.get(0);
        // 不能add
        list3.add(5);   // compile error
        list3.add(new Object()); // compile error
    }
}

小结:

  • 通配符上界? extends A, 表明所有的是A的类或者子类型可以传入,比如本例中的Integer和Double都是Number的子类,String不是。
  • 通配符上界? extends A,确定了类型是A或者是A的子类,那么向集合容器get获取数据,肯定是它的上界类A,因为其他放的类都是A的子类,比如例子中的 Number o = list3.get(0);
  • 如果向通配符上界集合中添加元素时,会失败。 List, 说明容器可以容纳的是A或者A的子类,但A的子类有很多,不确定放哪个,为了安全性,就直接不让你add,比如例子中的list3.add(5); ,5虽然是Number的子类,依然不能add。

4、下界通配符(

  上界通配符:用 super关键字声明,表示类型为T类型及T类型的父类

 
//代表 可以传入的实参的类型是Integer或者Integer的父类类型

彻底理解java中泛型_第2张图片

public static void printList1(List list) {
        for (Object x:list) {
            System.out.println(x);
        }
    }
    public static void main(String[] args) {
        List list = new ArrayList<>();
        list.add(1);
        printList1(list);  // ok
        List list1 = new ArrayList<>();
        list1.add(1.0D);
        printList1(list1);  // compile error
        List list2 = new ArrayList<>();
        list2.add("1");
        printList1(list2); // compile error
        List list3 = list;
        // 不能用下界接收
        Integer o = list3.get(0); // compile error
        // 能add
        list3.add(5);   // ok
        list3.add(new Number(5)); // compile error
    }
  • 通配符上界? super A, 表明所有的是A的类或者A的父类可以传入。
  • 通配符上界? super A,确定了类型是A或者是A的父类,那么向集合容器get获取数据,无法确定是A还是A的某个父类,所以不能get, Integer o = list3.get(0); // compile error,比如例子中用Integer接收,万一list3中放的是Object类型,就凉凉了。
  • 如果向通配符下界集合中添加元素时,只能添加下届类的子类。比如例子中的:list3.add(5), list3的通配符是,说明该集合存放的是Integer或者Integer的子类,我只要向容器中放Integer和它的子类都是成立的。

源码中使用例子:

类Collections 中binarySearch方法 【List> list】入参list中存储对象A或者A的父类是Comparable子类都可以。

public static 
    int binarySearch(List> list, T key) {
        if (list instanceof RandomAccess || list.size()

可通过以下代码辅助理解 > 与>

public class Test2 {

    public static > void sort1(List list) {
        Collections.sort(list);
    }

    public static > void sort2(List list) {
        Collections.sort(list);
    }

    public static void t1() {
        List animals = new ArrayList();
        animals.add(new Animal(20));
        animals.add(new Animal(30));

        List dogs = new ArrayList();
        dogs.add(new Dog(5));
        dogs.add(new Dog(10));

        sort1(animals);
        // sort1(dogs); 会报错

        sort2(animals);
        sort2(dogs);
    }
}

class Animal implements Comparable {
    public int age;
    public Animal(int age) {
        this.age = age;
    }
    public int compareTo(Animal other) {
        return this.age - other.age;
    }
}
class Dog extends Animal {
    public Dog(int age) {
        super(age);
    }
}

5、T 和 ?的区别 

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

四、泛型常见面试问题 

1、泛型为什么不能使用基本数据类型

  泛型在编译阶段会进行泛型擦除,擦除为原始类型,但是object并不是基本数据类型的父类。

2、类型擦除后保留的原始类型

3、通过下边界 通配符 解决 泛型擦除问题

你可能感兴趣的:(java基础面试,java,开发语言)