java进阶篇01、泛型

本篇主要介绍java泛型的一些进阶使用和规则,主要包括以下两方面内容:

  1. 方法参数通配符使用规则;
  2. 创建泛型对象时使用通配符的使用规则;

首先看一下后面讲解中使用到的类关系,有助于理解后面代码逻辑;Apple继承自Fruit, Fruit继承自Food;Generic是一个普通的泛型类;

注: 代码中被 // 注释的语句说明编译不通过

public class Apple extends Fruit {
}

public class Fruit extends Food {
}

public class Generic<T> {
    private T data;

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
}
复制代码

一.方法参数通配符:

1.方法参数中使用 ? extends Xxx 通配符

public static void print1(Generic<? extends Fruit> generic){

}
复制代码

此时通配符 ? extends Fruit 限定了泛型上界为Fruit,所以 Generic<Fruit> 对象和 Generic<Apple> 对象可以作为参数传递给print1,但是 Generic<Food> 对象不能作为参数传入,因为 Food 是 Fruit 的父类,超出了泛型规定的上界;具体代码如下所示

Generic<Fruit> fruitGeneric1 = new Generic<>();
print1(fruitGeneric1);

Generic<Apple> appleGeneric1 = new Generic<>();
print1(appleGeneric1);

//    错误使用
//    Generic<Food> foodGeneric1 = new Generic<>();
//    print1(foodGeneric1);
复制代码

2.方法参数中使用 ? super Fruit 通配符

public static void print2(Generic<? super Fruit> generic){

}
复制代码

此时通配符 ? super Fruit 限定了泛型下界为 Fruit,所以Generic<Fruit>对象和Generic<Food>对象可以作为参数传递给print2,但是Generic<Apple>对象不能作为参数传入,因为 Apple 是 Fruit 的子类,超出了泛型规定的下界;具体代码如下所示

Generic<Fruit> fruitGeneric2 = new Generic<>();
print2(fruitGeneric2);

Generic<Food> foodGeneric2 = new Generic<>();
print2(foodGeneric2);

//    错误使用
//    Generic<Apple> appleGeneric2 = new Generic<>();
//    print2(appleGeneric2);
复制代码

二.通配符泛型对象的参数读取和修改

1.使用? extends Xxx创建通配符泛型对象

Generic<? extends Fruit> generic = new Generic<>();
Fruit fruit1 = new Fruit();
Apple apple1 = new Apple();
Food food1 = new Food();
//    generic.setData(fruit1);
//    generic.setData(apple1);
//    generic.setData(food1);
Fruit data1 = generic.getData();
复制代码

上述代码分析:使用 ? extends Fruit 规定了泛型上界为 Fruit,但是并不能确定泛型具体的类型,所以不能通过setData方法去修改任何类型的对象;但是可以通过getData方法确定获得对象类型为 Fruit ,因为java中向上类型转换是安全的,所以类型为Fruit肯定没有问题;所以用 ? extends Fruit 规定的泛型对参数的读取是安全的,但是不能对参数进行修改。这是重点,需要掌握!

2.使用? super Fruit创建通配符泛型对象

Generic<? super Fruit> generic2 = new Generic<>();
Fruit fruit2 = new Fruit();
Apple apple2 = new Apple();
Food food2 = new Food();
generic2.setData(fruit1);
generic2.setData(apple1);
//    generic2.setData(food1);
Object data2 = generic2.getData();
复制代码

上述代码分析:使用 ? super Fruit 规定了泛型下界为Fruit,所以说里面存取的元素是Fruit或者是Fruit的父类,所以我们在setData方法中可以传入Fruit或者是Fruit子类的对象,同样可以理解为向上转型是安全的;但是不能传入 Food 类型的对象,因为我们只能确定元素是 Fruit 或者是 Fruit 的父类,但是并不能确切的确认是哪一个父类,所以不能传 Food 类对象。同理getData方法只能确定取出的元素是Fruit或者是Fruit的父类,但是并不能确认是哪个父类,又因为Object是所有类的父类,所以用Object接受肯定是没有问题的。这是重点,需要掌握!

3.使用 ? 创建通配符泛型对象

Generic<?> generic3 = new Generic<>();
Fruit fruit3 = new Fruit();
Apple apple3 = new Apple();
Food food3 = new Food();
//    generic3.setData(fruit1);
//    generic3.setData(apple1);
//    generic3.setData(food1);
Object data3 = generic3.getData();
复制代码

上述代码分析:这种情况我们可以认为 ? 通配符没有进行限制,所以不能用setData方法操作任何对象,又因为Object是所有类的父类,所以使用 Object 接受是没问题的。

4.使用? extends Xxx创建通配符泛型对象使用分析

List<Integer> list0 = new ArrayList<>();
list0.add(100);
List<? extends Number> list = new LinkedList<>(list0);
//    list.add(10);
Integer number = (Integer) list.get(0);
System.out.println(number);
复制代码

上述代码分析,根据上面情况1对 ? extends xxx 的总结,可以直接得出list对象是参数读取安全的,但是不能进行参数修改。

5.使用 ? super Xxx 创建通配符泛型对象使用分析

List<? super Number> list2 = new LinkedList<>();
list2.add(10);
Integer object = (Integer) list2.get(0);
System.out.println(object);
复制代码

上述代码分析,根据上面情况2对 ? super xxx 的总结,可以直接得出 list2 对象可以进行参数修改,参数读取到的可以使用 Object 类型的。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享