木心

毕竟几人真得鹿,不知终日梦为鱼

导航

泛型(四)类型通配符

一、类型通配符

  1.1、下面的程序演示了一种需求:定义一个方法,该方法有一个集合形参,但是形参的元素类型是不确定的

public static void main(String[] args) {
    // 想要遍历打印List<String> list1,定义一个方法,比如print1()
    List<String> list = new ArrayList<>();
    list.add("aaa");
    list.add("bbb");
    print1(list);
    
    // 现在想要遍历打印List<Integer> list2,就要重新定义一个方法,比如print2()
    List<Integer> list2 = new ArrayList<>();
    list2.add(111);
    list2.add(222);
    print2(list2);
}

public static void print1(List<String> list) {
    for (String str : list) {
        System.out.println(str);
    }
}

public static void print2(List<Integer> list) {
    for (Integer i: list) {
        System.out.println(i);
    }
}

 

  1.2、使用类型通配符可以解决:List<?> list

public static void main(String[] args) {
    List<String> list = new ArrayList<>();
    list.add("aaa");
    list.add("bbb");
    print3(list);
    
    List<Integer> list2 = new ArrayList<>();
    list2.add(111);
    list2.add(222);
    print3(list2);
}

public static void print3(List<?> list) {
    for (Object obj : list) {
        System.out.println(obj);
    }
}

 

  1.3、使用类型通配符的局限性(弊端)

  声明List<?> list后,不能向集合中添加元素,因为无法确定集合的元素类型。唯一例外的是null,它是所有引用类型的实例。

public static void main(String[] args) {
    List<String> list = new ArrayList<>();
    list.add("aaa");
    list.add("bbb");
    print3(list);
}

public static void print3(List<?> list) {
    // list.add("ccc"); // 错误,由于不知道元素的类型所以无法将 "ccc" 添加进集合
    // list.add(111); // 错误,由于不知道元素的类型所以无法将 111 添加进集合
    // list.add(new Object()); // 错误,由于不知道元素的类型所以无法将Object对象添加进集合
    list.add(null); // 没问题
    
    for (Object obj : list) {
        System.out.println(obj);
    }
}

 

public static void main(String[] args) {
    List<?> list = new ArrayList<>();
    // list.add("ccc"); // 错误,由于不知道元素的类型所以无法将 "ccc" 添加进集合
    // list.add(111); // 错误,由于不知道元素的类型所以无法将 111 添加进集合
    // list.add(new Object()); // 错误,由于不知道元素的类型所以无法将Object对象添加进集合
    list.add(null); // 没问题
}

 

  另一方面,可以调用get()方法来返回List<?>集合指定索引的元素,虽然不知道元素的类型,但是可以确定的是,元素一定是Object类型。

public static void main(String[] args) {
    List<String> list = new ArrayList<>();
    list.add("aaa");
    list.add("bbb");
    list.add(null);
    print3(list);
}

public static void print3(List<?> list) {
    // 可以调用get()方法返回集合的元素
    for (int i =0; i < list.size(); i++) {
        Object obj = list.get(i);
        System.out.println(obj);
    }
}

 

  总结:当使用通配符时,对泛型类中的参数为泛型的方法起到副作用,不能再使用。对于返回值为泛型的方法,只能赋值给Object类型变量。

 

二、类型通配符的限定

  2.1、指定类型通配符的上限 -- 返回值为泛型的方法可以使用了

public static void main(String[] args) {
    List<Integer> intList = new ArrayList<>();
    List<Long> longList = new ArrayList<>();
    print(intList);
    print(longList);
}

// ? extends Number:指定通配符的上限,即调用本方法传递的List后尖括号里的类型只能是Number及其子类
public static void print(List<? extends Number> list) {
    // 因为调用本方法时传递的实参可能是List<Long>也可能是List<Integer>或其他,即还是不能确定实参具体的类型,所以下面代码错误
    // list.add(new Integer("100")); 
    list.add(null); // 没问题
    
    // 可以调用get()方法返回集合的元素
    for (int i =0; i < list.size(); i++) {
        Number obj = list.get(i);
        System.out.println(obj);
    }
}

 

  2.2、指定类型通配符的下限 -- 参数为泛型的方法可以使用了

public static void main(String[] args) {
    List<Integer> intList = new ArrayList<>();
    List<Number> numList = new ArrayList<>();
    List<Object> objList = new ArrayList<>();
    print(intList);
    print(numList);
    print(objList);
}

// ? super Number:指定通配符的下限,即调用本方法传递的List后尖括号里的类型只能是Integer及其父类
public static void print(List<? super Integer> list) {
    // 参数为泛型的方法可以使用了
    list.add(new Integer("100"));
    // list.add(new Object()); // 错误
    list.add(null); // 没问题
    
    // 返回值为泛型的方法还是不能使用(或说是只能赋值给Object类型)
    for (int i =0; i < list.size(); i++) {
        // Integer obj = list.get(i);// 错误
        Object obj = list.get(i);
        System.out.println(obj);
    }
}

 

  总结:

 

posted on 2019-05-16 23:04  wenbin_ouyang  阅读(542)  评论(0编辑  收藏  举报