当前位置: 首页 > article >正文

Java泛型之通配符类型

1. 案例分析

class Animal {
    @Override
    public String toString() {
        return "animal";
    }
}

class Cat extends Animal {
    @Override
    public String toString() {
        return "cat";
    }
}

class Dog extends Animal {
    @Override
    public String toString() {
        return "dog";
    }
}

public class App {
    public static void show(ArrayList<Animal> animals) {
        for(Animal a : animals) {
            System.out.println(a);
        }
    }

    public static void main(String[] args) {
        ArrayList<Cat> list = new ArrayList<Cat>();
        list.add(new Cat());
        show(list);
        list.get(0).getCat();
    }
}

在main方法中, 构造了一个ArrayList类的对象, 调用show方法打印该对象中的元素。 一切看起来都很正常, 结果在编译时, 编译器报错如下:

在这里插入图片描述
[分析]:
    Cat是Animal的子类, ArrayList<Cat>理应是ArrayList<Animal>的子类了, 为何还提示“不兼容的类型”? 其实这只不过是我们想当然而已,假如ArrayList<Cat>可以转型为ArrayList<Animal>, 那么利用后者就可以添加任何派生于Animal的对象了, 如Dog类的对象, 这就失去了ArrayList<Cat>原本的目的了, 因此编译器才会给我们报告错误。
    那么ArrayList<Cat>ArrayList<Dog>, 甚至ArrayList<String>, 它们有没有公共的父类型呢? 答案是有, 那就是ArrayList<?>"?" 是通配符, 代表任意类型。

好,修改代码,将show()中的参数修改为ArrayList<?>类型。
同时注意,在show()中,由于元素类型是未知的,所以不能用for(Animal a : animals)来循环,但是所有的对象类型都是Object的子类,所以可以使用Object来替代,否则报错(不兼容的类型, capture#1, 共 ?无法转换为Animal)。

public static void show(ArrayList<?> animals) {
        for(Object a : animals) {
            System.out.println(a);
        }
    }

此时编译,一切正常运行。

[注意]:
    再来关注一下 void show(ArrayList<?> animals) 函数,由于使用了通配符类型, 因此不能往animals中写入任何对象, 毕竟元素的类型是未知的, 如果允许你写入, 那岂不是可以写入任意类型的对象了? 显然, 这是不允许的。 同时也不能调用animals中存储的对象的方法(从Object类继承的方法除外) , 因为类型都不确定, 又怎么能知道这些方法有没有呢? 例如下面的代码在编译期间就会报错。

public static void show(ArrayList<?> animals) {
        animals.add(new Dog());		// error
        animals.get(0).showCat();	// error
        // ... 
}

在这里插入图片描述


2. 通配符的子类型限定(<? extends T>)

    "?"这种通配符貌似功能很强大, 可以匹配任何类型, 但实际上,你会发现什么都做不了。 其实, 我们可以对通配符做限定, 来缩小通配符匹配的范围。 例如, 对于上面的例子, 我们希望是任何Animal类的子类型, 而不是所有类型, 于是可以用通配符: ? extends Animal来表示。 要注意的是, 这里的extends和类继承的extends并不是一个含义, 前者只是表示Animal或者任何Animal的子类型, extends后的类型也可以是接口类型。
    对通配符进行限定后就比较明确了, ArrayList<? extends Animal>ArrayList<Cat>ArrayList<Dog>的父类型, 而不是ArrayList<String>ArrayList<Double>...的父类型。

abstract class Shape {
    public abstract void draw(Canvas c);
}

class Circle extends Shape {
    private int num;

    public Circle(int num) {
        this.num = num;
    }

    @Override
    public void draw(Canvas c) {
        System.out.println("draw circle " + this.num + ".");
    }
}

class Rectangle extends Shape {
    private int num;

    public Rectangle(int num) {
        this.num = num;
    }

    @Override
    public void draw(Canvas c) {
        System.out.println("draw rectangle " + this.num + ".");
    }
}

class Canvas {
    public void drawAll(ArrayList<? extends Shape> list) {
        for(Shape shape : list) {
            shape.draw(this);
        }
    }
}

public class App {

    public static void main(String[] args) {
        ArrayList<Circle> circleList = new ArrayList<Circle>();
        circleList.add(new Circle(111));
        circleList.add(new Circle(222));

        ArrayList<Rectangle> rectList = new ArrayList<Rectangle>();
        rectList.add(new Rectangle(333));
        rectList.add(new Rectangle(444));

        Canvas can = new Canvas();
        can.drawAll(circleList);
        can.drawAll(rectList);
    }
}

3. 通配符的超类型限定(<? super T>)

未完待续。。。


http://www.kler.cn/a/15079.html

相关文章:

  • 【Linux】Linux入门实操——进程管理(重点)
  • 鸿蒙实战:页面跳转传参
  • iceberg小文件合并策略
  • Android CPU核分配关联进程
  • 最优化方法_罚函数法例题
  • 信息与决策支持系统(Information and Decision Support Systems,IDSS)
  • Linux 内存 pt.1
  • 正则表达式基础
  • 【Java】SpringBoot中实现多数据源切换
  • Vue-列表过滤
  • 题解,超星进程
  • 记录 docker linux部署jar
  • 三问 ThreadLocal —— 有什么用 ? 使用时有什么潜在风险?原理 ?
  • 【C++】模板进阶
  • mysql实现存在则保存,不存在则更新
  • Java线程池核心参数
  • CA(证书颁发机构)
  • 软件测试概念
  • MATLAB-Lingo求解线性规划问题-奶制品2
  • 车联网V2X通信技术及应用介绍
  • 备忘录设计模式解读
  • 实用的股票接口,股票api收藏(11)
  • 03.预处理
  • 轻量级网络EfficientNetB0,利用迁移学习中的微调技术进行小样本轴承故障诊断(Python代码,带有数据集,训练集集的每类只需10个样本)
  • 【接口自动化测试】selenium旗舰版Web测试理论篇
  • 听我一句劝,别去外包,干了三年,废了....