Java学习之泛型


一、泛型是什么?

泛型:允许在定义类、接口时通过一个标识表示类中某个属性的类型或某个方法的返回之及参数类型,这个类型,例如ArrayList,然后在代码中为用到的类创建对应的ArrayList<类型>

  • 需要强制转型;
  • 不方便,易出错
ArrayList list = new ArrayList();
list.add("Hello");
// 获取到Object,必须强制转型为String:
String first = (String) list.get(0);

list.add(new Integer(123));
// ERROR: ClassCastException:
String second = (String) list.get(1);
ArrayList<String> strList = new ArrayList<String>();

泛型的好处:使用时不必对类型进行强制转换,它通过编译器对类型进行检查;

import org.junit.Test;
import java.util.*;
/**
 * @Author: mei_ming
 * @DateTime: 2022/9/21 22:15
 * @Description: TODO
 */
public class GenericTest {

    //在集合中使用泛型之前
    //  需要强制转型;
    //  不方便,易出错。
    @Test
    public void test(){
        ArrayList list = new ArrayList();
        //需求:存放学生的成绩
        list.add(78);
        list.add(99);
        list.add(59);
        list.add(67);
        //错误数据
        list.add("Tom");  //java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer

        for (Object score:list) {
            int stuScore = (Integer)score;
            System.out.println(stuScore);
        }
    }
}

二、在集合中使用泛型

改进上述代码:

import org.junit.Test;
import java.util.*;

/**
 * @Author: mei_ming
 * @DateTime: 2022/9/21 22:15
 * @Description: TODO
 */
public class GenericTest {
    //List:
    @Test
    public void test2(){
        ArrayList<Integer> list = new ArrayList<Integer>();
        //需求:存放学生的成绩
        list.add(78);
        list.add(99);
        list.add(59);
        list.add(67);
        //错误数据
        //list.add("Tom");  //编译时期 进行类型检查,保证数据安全

        //方式一:
        for (Integer score:list) {
            int stuScore = score;
            System.out.println(stuScore);
        }
        //方式二:
        Iterator<Integer> iterator = list.iterator();
        //由于List中定义public Iterator<E> iterator() 所以iterator的类型为Integer
        while(iterator.hasNext()){
            int stuScore = iterator.next();
            System.out.println(stuScore);
        }
    }

    //Map:
    @Test
    public void test3(){
        Map<String, Integer> map = new HashMap<String, Integer>();
        map.put("Tom",23);
        map.put("Jerry",24);
        map.put("Jack",44);

        //map.put(22,"Rose");  //编译check

        //泛型的嵌套
        Set<Map.Entry<String, Integer>> entries = map.entrySet();
        Iterator<Map.Entry<String, Integer>> iterator = entries.iterator();
        while(iterator.hasNext()){
            Map.Entry<String, Integer> next = iterator.next();
            String key = next.getKey();
            Integer value = next.getValue();
            System.out.println(key+" "+value);
        }
        /** 结果:
         *  Tom 23
         *  Jerry 24
         *  Jack 44
         */
    }

小结

  1. 集合接口或集合类在jdk5.0时都修改为带泛型的结构。
  2. 在实例化集合类时,可以指明具体的泛型泛型。
  3. 指明完以后,在集合类或接口中凡是定义类/接口时,内部结构使用到类的泛型的位置,都指定为实例化的泛型类型。 比如 add(E e) --> 示例化以后 : add(Integer e)
  4. 注意点:泛型的类型必须时类,不能时基本数据类型,需要用到基本数据类型的位置,拿包装类替换
  5. 如果实例化时,没写泛型。默认为Object型

举例:
加上了泛型后,代码的简化

import org.junit.Test;
import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;

/**
 * @Author: mei_ming
 * @DateTime: 2022/9/22 21:27
 * @Description: 测试类
 */
public class EmployeeTest {

    //按name进行自然排序 升序
    @Test
    public void test1(){
        TreeSet<Employee> employees = new TreeSet<>();
        employees.add(new Employee("Tom",1001,new MyDate(1999,2,13)));
        employees.add(new Employee("Jerry",1002,new MyDate(1996,2,12)));
        employees.add(new Employee("Rose",1003,new MyDate(1996,3,13)));
        employees.add(new Employee("Jack",1004,new MyDate(1996,2,13)));
        Iterator<Employee> iterator = employees.iterator();
        while(iterator.hasNext()){
            Employee employee = iterator.next();
            System.out.println(employee);
        }
        /**
         * Employee{name='Jack', id=1004, birthday=MyDate{year=1996, month=2, day=13}}
         * Employee{name='Jerry', id=1002, birthday=MyDate{year=1996, month=2, day=12}}
         * Employee{name='Rose', id=1003, birthday=MyDate{year=1996, month=3, day=13}}
         * Employee{name='Tom', id=1001, birthday=MyDate{year=1999, month=2, day=13}}
         */
    }

    //按生日进行定制排序 升序
    @Test
    public void test2(){
        TreeSet<Employee> employees = new TreeSet<>(new Comparator<Employee>() {
            @Override
            public int compare(Employee o1, Employee o2) {
                MyDate b1=o1.getBirthday();
                MyDate b2 = o2.getBirthday();
                return b1.compareTo(b2);
            }
        });
        employees.add(new Employee("Tom",1001,new MyDate(1999,2,13)));
        employees.add(new Employee("Jerry",1002,new MyDate(1996,2,12)));
        employees.add(new Employee("Rose",1003,new MyDate(1996,3,13)));
        employees.add(new Employee("Jack",1004,new MyDate(1996,2,13)));
        Iterator<Employee> iterator = employees.iterator();
        while(iterator.hasNext()){
            Employee employee = iterator.next();
            System.out.println(employee);
        }
        /**
         * Employee{name='Jerry', id=1002, birthday=MyDate{year=1996, month=2, day=12}}
         * Employee{name='Jack', id=1004, birthday=MyDate{year=1996, month=2, day=13}}
         * Employee{name='Rose', id=1003, birthday=MyDate{year=1996, month=3, day=13}}
         * Employee{name='Tom', id=1001, birthday=MyDate{year=1999, month=2, day=13}}
         */
    }
}
/**
 * @Author: mei_ming
 * @DateTime: 2022/9/22 21:27
 * @Description: Employee 
 */
public class Employee implements Comparable<Employee> {

    private String name;
    private int id;
    private MyDate birthday;

    public Employee(String name, int id, MyDate birthday) {
        this.name = name;
        this.id = id;
        this.birthday = birthday;
    }

    public Employee() {
    }

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", id=" + id +
                ", birthday=" + birthday +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public MyDate getBirthday() {
        return birthday;
    }

    public void setBirthday(MyDate birthday) {
        this.birthday = birthday;
    }

    @Override
    public int compareTo(Employee o) {
        return this.name.compareTo(o.name);
    }
}
/**
 * @Author: mei_ming
 * @DateTime: 2022/9/22 21:35
 * @Description: MyDate 
 */
public class MyDate implements Comparable<MyDate> {
    private int year;
    private int month;
    private int day;

    public MyDate(int year, int month, int day) {
        this.year = year;
        this.month = month;
        this.day = day;
    }

    @Override
    public String toString() {
        return "MyDate{" +
                "year=" + year +
                ", month=" + month +
                ", day=" + day +
                '}';
    }

    public MyDate() {
    }

    public int getYear() {
        return year;
    }

    public void setYear(int year) {
        this.year = year;
    }

    public int getMonth() {
        return month;
    }

    public void setMonth(int month) {
        this.month = month;
    }

    public int getDay() {
        return day;
    }

    public void setDay(int day) {
        this.day = day;
    }

    @Override
    public int compareTo(MyDate o) {
        //比较年
        int minYear = this.getYear() - o.getYear();
        if (minYear != 0) {
            return minYear;
        }
        //比较月
        int minMonth = this.getMonth() - o.getMonth();
        if (minMonth != 0) {
            return minMonth;
        }
        //比较日
        return this.getDay() - o.getDay();

    }
}


三、自定义泛型结构

  1. 泛型类可能有多个参数,此时应将多个参数一起放在尖括号内,比如<E1,E2,E3>
  2. 泛型类的构造器如下: public Order(){}
    错误写法:public Order<E>(){}
  3. 实例化后,操作原来泛型位置的结构必须与指定泛型一致
  4. 泛型不同,不能相互赋值
    尽管在编译时ArrayList<String>和ArrayList<Integer>是来那种类型,但是,在运行时,只有一个ArrayList 被加载到JVM中
  5. 泛型如果不指定,将被擦除,泛型对应的类型均按照Object处理,但不等价于Object,经验:泛型要使用就一路都用
  6. 如果泛型结构是一个接口或抽象类,则不可创建泛型类的对象
  7. 泛型的指定中不能用基本数据类型,可以使用包装类
  8. 在类/接口上声明的泛型,在本类中即代表某种类型,可以作为非静态属性的类型,非静态方法的参数类型,非静态方法的返回值类型。但在静态方法中不能使用类的泛型
  9. 异常类中不能使用泛型
    10.不能用new E[10] 可以用 E[] eles = (E[]) new Object[10]
import org.junit.Test;
/**
 * 泛型类,泛型接口,泛型方法
 */
/**
 * @Author: mei_ming
 * @DateTime: 2022/9/21 22:15
 */
public class GenericTest2 {

    @Test
    public void test(){
        //不使用泛型,认为此泛型为Object
        //不推荐
        Order order = new Order();
        order.setOrderT(123);
        order.setOrderT("123");

        //推荐,加泛型
        Order<String> stringOrder = new Order<>();
        stringOrder.setOrderT("123");
    }

    @Test
    public void test2(){
        //子类继承方式1:  子类继承时指明泛型为Integer,子类是普通类
        SubOrder1 subOrder1 = new SubOrder1();
        subOrder1.setOrderT(123);

        //子类继承方式2:
        SubOrder2<String> subOrder2 = new SubOrder2<>();
        subOrder2.setOrderT("123");
    }
}
/**
 * @Author: mei_ming
 * @DateTime: 2022/9/22 22:11
 * @Description: Order
 */
public class Order<T> {
    private int id;
    private String name;
    private T orderT;

    public Order(int id, String name, T orderT) {
        this.id = id;
        this.name = name;
        this.orderT = orderT;
    }

    public Order() {
    }

    @Override
    public String toString() {
        return "Order{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", orderT=" + orderT +
                '}';
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public T getOrderT() {
        return orderT;
    }

    public void setOrderT(T orderT) {
        this.orderT = orderT;
    }
}
public class SubOrder1 extends Order<Integer>{  //普通类
}
public class SubOrder2<T> extends Order<T> { //泛型类
}

泛型方法

import org.junit.Test;
import java.util.List;

/**
 * 泛型类,泛型接口,泛型方法
 */

/**
 * @Author: mei_ming
 * @DateTime: 2022/9/21 22:15
 */
public class GenericTest2 {
    //测试泛型方法
    @Test
    public void test3(){
        Order<String> order = new Order<>();
        Integer[] arr = new Integer[]{1,2,3,4};

        //泛型方法在调用时,指明泛型参数的类型
        List<Integer> integerList = order.copyFromArrayToList(arr);
        System.out.println(integerList); //[1, 2, 3, 4]
    }
}
import java.util.ArrayList;
import java.util.List;
/**
 * @Author: mei_ming
 * @DateTime: 2022/9/22 22:11
 * @Description: Order
 */
public class Order<T> {
    private int id;
    private String name;
    private T orderT;

    public Order(int id, String name, T orderT) {
        this.id = id;
        this.name = name;
        this.orderT = orderT;
    }

    public Order() {
    }

    @Override
    public String toString() {
        return "Order{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", orderT=" + orderT +
                '}';
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public T getOrderT() {
        return orderT;
    }

    public void setOrderT(T orderT) {
        this.orderT = orderT;
    }

//    编译报错:普通静态方法不能调用泛型,因为类初始化时,不知道T的具体类型
//    public static void functionA(T t){
//
//    }

    //泛型方法: 在方法中出现了泛型结构,泛型参数与类的泛型参数没有任何关系
    //换句话说,泛型方法所属的类是不是泛型类都没有关系
    //泛型方法,可以声明为静态的,原因:泛型参数是在调用方法时确定的,并非在实例化类时确定的
    public static <E> List<E> copyFromArrayToList(E[] arr){
        ArrayList<E> list = new ArrayList<>();
        for(E e : arr){
            list.add(e);
        }
        return list;
    }
}

import java.util.ArrayList;
import java.util.List;

/**
 * @Author: mei_ming
 * @DateTime: 2022/9/22 22:17
 * @Description: TODO
 */
public class SubOrder1 extends Order<Integer>{  //普通类

    //泛型方法: 在方法中出现了泛型结构,泛型参数与类的泛型参数没有任何关系
    //换句话说,泛型方法所属的类是不是泛型类都没有关系
    public static <E> List<E> copyFromArrayToList(E[] arr){
        ArrayList<E> list = new ArrayList<>();
        for(E e : arr){
            list.add(e);
        }
        return list;
    }
}


四、泛型在继承上的体现

import org.junit.Test;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.List;
/**
 * @Author: mei_ming
 * @DateTime: 2022/9/24 13:52
 */
public class GenericTest {
    /**
     * 1. 泛型在继承方面的体现
     * 类A是类B的父类,G<A> 和 G<B> 二者不具备父子类关系,二者是并列关系
     */
    @Test
    public void test1() {
        Object obj = null;
        String str = null;
        obj = str;

        Object[] arr1 = null;
        String[] arr2 = null;
        arr1 = arr2;

        List<Object> list1 = null;
        List<String> list2 = null;
        //报错,此时的list1和list2的类型不具有子父类关系
//        list1=list2;

        /**
         * 反证法
         * 假设list1=list2;
         * list1.add(123); 导致list2 混入非String的数据,出错
         */
    }

    /**
     * 类A是类B的父类,A<G>是B<G>的父类
     */
    @Test
    public void test2() {
        AbstractList<String> list1 = null;
        List<String> list2 = null;
        ArrayList<String> list3 = null;

        list1=list3;
        list2=list3;
        
    }
}


五、通配符的使用

无限定通配符

import org.junit.Test;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * @Author: mei_ming
 * @DateTime: 2022/9/24 13:52
 */
public class GenericTest {
    /**
     * 2. 通配符<?>的使用
     * 类A是类B的父类, G<A> 和 G<B> 二者不具备父子类关系,
     * 二者共同的父类是 G<?>
     */

    @Test
    public void test3() {
        List<Object> list1 = null;
        List<String> list2 = null;
        List<?> list = null;

        list = list1;
        list = list2;

        print(list1);
        print(list2);
    }

    // 此方法list1能遍历,list2也能遍历
    public void print(List<?> list) {
        Iterator<?> iterator = list.iterator();
        while (iterator.hasNext()) {
            Object next = iterator.next();
            System.out.println(next);
        }
    }

    @Test
    public void test4() {
        List<?> list = null;
        List<String> list1 = new ArrayList<>();
        list1.add("AA");
        list1.add("BB");
        list = list1;

        //测试 添加(写入): 对于List<?> 就不能向其内部添加数据
        //除了null之外
//        list.add("CC");//编译出错
        list.add(null);

        //测试 读取 :运行读取数据,读取的数据类型为Object
        Object o = list.get(0);
        System.out.println(o);  // "AA"
    }
}

  1. 使用类型通配符:?
    比如:List<?>, Map<?,?>
    List<?>List<String>List<Object>等各种泛型List的父类
  2. 读取List<?>的对象list中的元素时,永远是安全的,因为不管list的真实类型是什么,他包含的都是Object
  3. 写入List中的元素时,不行,因为我们不知道 ? 的元素类型,我们不能向其中添加对象。唯一的例外时null,它是所有类型的元素
Collection<?> c = new ArrayList<String>();
c.add(new Object()); //编译时错误

因为我们不知道c的元素类型,我们不能向其中添加对象,add方法有类型参数E 作为集合的元素类型,我们传给add的任何参数都必须是一个未知类型的子类,因为我们不知道那个是什么类型,所以我们无法传任何东西进去

有限制条件通配符

  • <?> :运行所有泛型的引用调用
  • 通配符指定上限:上限extends,使用是指定的类型必须是继承某个类,或者实现某个接口,即 <=
  • 通配符指定下限:下限super,使用时指定的类型不能小于操作的类,即 >=

举例:
<? extends Number>(无穷小,Number]
只允许泛型为Number及Number子类的引用调用
<? super Number> [Number,无穷大)
只允许泛型为Number及Number父类的引用调用
<? extends Comparable>
只允许泛型为实现Comparable接口的实现类的引用调用

extends

  • 使用类似<? extends Number>通配符作为方法参数时表示:(只能读不能写)
    • 方法内部可以调用获取Number引用的方法,例如:Number n = obj.getFirst();
    • 方法内部无法调用传入Number引用的方法(null除外),例如:obj.setFirst(Number n);
/**
 * @Author: mei_ming
 * @DateTime: 2022/9/24 17:43
 * @Description: TODO
 */
public class GenericTest3 {

    public static void main(String[] args) {
        Pair<Integer> p = new Pair<>(123, 456);
        int sum1 = add(p);
        System.out.println(sum1);

        int sum2 = add2(p);
        System.out.println(sum2);
    }

    // get()
    static int add(Pair<? extends Number> p) {
        // compile accept!
        Number first = p.getFirst();
        Number last = p.getLast();
        // compile error!
//        Integer first = p.getFirst();
//        Integer last = p.getLast();
        /**
         * 因为实际的返回类型可能是Integer,也可能是Double或者其他类型,
         * 编译器只能确定类型一定是Number的子类(包括Number类型本身),
         * 但具体类型无法确定。
         */
        return first.intValue() + last.intValue();
    }
    // set() [除了null以外]
    static int add2(Pair<? extends Number> p){
        Number first = p.getFirst();
        Number last = p.getLast();
        // compile error!
//        p.setFirst(new Integer(first.intValue() + 100));
//        p.setLast(new Integer(last.intValue() + 100));
        // compile accept!
        p.setFirst(null);
        /**
         * 原因:
         *   原因还在于擦拭法。如果我们传入的p是Pair<Double>,
         *   显然它满足参数定义Pair<? extends Number>,
         *   然而,Pair<Double>的setFirst()显然无法接受Integer类型。
         *
         * <? extends Number> 通配符的一个重要限制:
         *     1. 方法参数签名setFirst(? extends Number)无法传递任何Number的子类型
         *          给setFirst(? extends Number)。
         *     2. 这里唯一的例外是可以给方法参数传入null:
         */
        return p.getFirst().intValue() + p.getFirst().intValue();
    }
}
class Pair<T> {
    private T first;
    private T last;

    public Pair(T first, T last) {
        this.first = first;
        this.last = last;
    }

    public T getFirst() {
        return first;
    }
    public T getLast() {
        return last;
    }
    public void setFirst(T first) {
        this.first = first;
    }
    public void setLast(T last) {
        this.last = last;
    }
}
  • 使用类似<T extends Number>定义泛型类时表示: (无穷小,Number]
    • 泛型类型限定为Number以及Number的子类。
/**
 * @Author: mei_ming
 * @DateTime: 2022/9/24 17:57
 */
class Pair<T extends Number> {
    private T first;
    private T last;

    public Pair(T first, T last) {
        this.first = first;
        this.last = last;
    }

    public T getFirst() {
        return first;
    }
    public T getLast() {
        return last;
    }
    public void setFirst(T first) {
        this.first = first;
    }
    public void setLast(T last) {
        this.last = last;
    }
}
public class GenericTest4 {

    public static void main(String[] args) {
        // compile accept!
        Pair<Number> p1 = null;
        Pair<Integer> p2 = null;
        Pair<Double> p3 = null;

        // compile error!
//        Pair<String> p1 = null;
//        Pair<Object> p2 = null;

        /**
         * 因为String、Object都不符合<T extends Number>,
         *   它们不是Number类型或Number的子类。
         */
    }
}

super

  • 使用类似<? super Integer>通配符作为方法参数时表示:(只能写不能读)
    • 方法内部可以调用传入Integer引用的方法,例如:obj.setFirst(Integer n);
    • 方法内部无法调用获取Integer引用的方法(Object除外),例如:Integer n = obj.getFirst();
/**
 * @Author: mei_ming
 * @DateTime: 2022/9/24 18:03
 * @Description: TODO
 */

/**
 * Pair<? super Integer>表示,
 *  方法参数接受所有泛型类型为Integer或Integer父类的Pair类型。
 */
class Pair<T> {
    private T first;
    private T last;

    public Pair(T first, T last) {
        this.first = first;
        this.last = last;
    }

    public T getFirst() {
        return first;
    }
    public T getLast() {
        return last;
    }
    public void setFirst(T first) {
        this.first = first;
    }
    public void setLast(T last) {
        this.last = last;
    }
}

public class GenericTest5 {

    public static void main(String[] args) {
        Pair<Number> p1 = new Pair<>(12.3, 4.56);
        Pair<Integer> p2 = new Pair<>(123, 456);
        setSame(p1, 100);
        setSame(p2, 200);
        System.out.println(p1.getFirst() + ", " + p1.getLast());
        System.out.println(p2.getFirst() + ", " + p2.getLast());
    }

    //set() 正常传值
    static void setSame(Pair<? super Integer> p, Integer n) {
        p.setFirst(n);
        p.setLast(n);
        Object first = p.getFirst();
    }
    // get() [除了Object类型]
    static void setSame2(Pair<? super Integer> p, Integer n) {
        // compile error!
//        Integer first = p.getFirst();
        // compile accept!
        Object first = p.getFirst();
        /**
         * 因为如果传入的实际类型是Pair<Number>,
         *  编译器无法将Number类型转型为Integer。
         *
         * 唯一可以接收getFirst()方法返回值的是Object类型:
         */
    }
}

  • 只允许泛型为Number及Number父类的引用调用 [Number,无穷大)
/**
     * 3.有限制条件的通配符的使用
     *  ? extends A:
     *      G<? extends A> 可以作为G<A>和G<B>的父类,其中B是A的子类
     *  ? super A:
     *      G<? super A> 可以作为G<A>和G<B>的父类,其中B是A的父类
     */
    @Test
    public void test5(){
        List<? extends Order> list1 =null;
        List<? super Order> list2 = null;

        List<SubOrder1> list3= new ArrayList<>();
        List<Order> list4 = new ArrayList<>();
        List<Object> list5 = new ArrayList<>();

        list1=list3;
        list1=list4;
//        list1=list5;  //编译出错

//        list2=list3;  //编译出错
        list2=list4;
        list2=list5;

        // 读取数据:
        list1=list3;
        Order order = list1.get(0);
//        SubOrder1 subOrder1 = list1.get(0);  //编译不通过
    }

使用extends和super通配符要遵循PECS原则。
即:Producer Extends Consumer Super,
如果需要返回T,它是生产者(Producer),要使用extends通配符;如果需要写入T,它是消费者(Consumer),要使用super通配符。

//以Collections的copy()方法为例:

public class Collections {
    public static <T> void copy(List<? super T> dest, List<? extends T> src) {
        for (int i=0; i<src.size(); i++) {
            T t = src.get(i); // src是producer
            dest.add(t); // dest是consumer
        }
    }
}
//需要返回T的src是生产者,因此声明为List<? extends T>,
//需要写入T的dest是消费者,因此声明为List<? super T>。

擦拭法

import org.junit.Test;
/**
 * @Author: mei_ming
 * @DateTime: 2022/9/24 16:00
 */
class Pair<T> {
    private T first;
    private T last;
    public Pair(T first, T last) {
        this.first = first;
        this.last = last;
    }
    public T getFirst() {
        return first;
    }
    public T getLast() {
        return last;
    }
}

public class GenericTest2 {
    /**
     * 擦拭法:所谓擦拭法是指,虚拟机对泛型其实一无所知,所有的工作都是编译器做的。
     */

    //局限一:<T>不能是基本类型,例如int,因为实际类型是Object,Object类型无法持有基本类型:
    @Test
    public void test(){
//        Pair<int> p = new Pair<>(1, 2);  // compile error!
    }

    //局限二:无法取得带泛型的Class。
    @Test
    public void test2(){
        Pair<String> p1 = new Pair<>("Hello", "world");
        Pair<Integer> p2 = new Pair<>(123, 456);
        Class c1 = p1.getClass();
        Class c2 = p2.getClass();
        System.out.println(c1==c2); // true
        System.out.println(c1==Pair.class); // true
        /**
         * 因为T是Object,我们对Pair<String>和Pair<Integer>类型获取Class时,获取到的是同一个Class,
         * 也就是Pair类的Class。
         * 换句话说,所有泛型实例,无论T的类型是什么,getClass()返回同一个Class实例,
         * 因为编译后它们全部都是Pair<Object>。
         */
    }

    //局限三:无法判断带泛型的类型:
    @Test
    public void test3(){
        Pair<Integer> p = new Pair<>(123, 456);
        // Compile error:
//        if (p instanceof Pair<String>) {
//        }
        // Compile accept:
        if (p instanceof Pair) {
        }
        //原因和前面一样,并不存在Pair<String>.class,而是只有唯一的Pair.class。
    }

    //局限四:不能实例化T类型:
//    public class Pair<T> {
//        private T first;
//        private T last;
//        public Pair() {
//            // Compile error:
            first = new T();
            last = new T();
//        }
//    }
    /**
     *擦拭后实际上变成了:
     * first = new Object();
     * last = new Object();
     * 这样一来,创建new Pair<String>()和 创建new Pair<Integer>()就全部成了Object
     */

    //要实例化T类型,我们必须借助额外的Class<T>参数:
//    public class Pair<T> {
//        private T first;
//        private T last;
//        public Pair(Class<T> clazz) {
//            first = clazz.newInstance();
//            last = clazz.newInstance();
//        }
//    }
//    Pair<String> pair = new Pair<>(String.class);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值