泛型协变

问题

需要对中间件、应用2个类型的数据进行domain的数据进行解析,2应用都继承自CmdbBaseApp,此时parseDomain的时候接受参数 List<cmdbBaseApp> 类型,同时处理2种类型数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class CmdbBaseApp {
private int id;
private String domain;
}

public class CmdbMiddleResponse extends CmdbBaseApp {
}

public class CmdbProjectResponse extends CmdbBaseApp {
}


private void parseDomain(List<CmdbBaseApp> apps) {
}

现象

编译报错

1
2
3
4
5
// 调用方法传入子类型时报错
parseDomain(new ArrayList<CmdbMiddleResponse>());


java: 不兼容的类型: java.util.ArrayList<com.dada.alarm.model.CmdbMiddleResponse>无法转换为java.util.List<com.dada.alarm.model.CmdbBaseApp>

解析

由于数组是协变的,泛型是不变的,所以 List<CmdbMiddleResponse> 不等于 List<CmdbBaseApp>

数组是协变的,泛型是不变的。泛型可以通过通配符实现协变和逆变(pecs)。

《Effective java》
1
2
3
4
5
6
7
8
9
10
11
12
13
解法一: 利用数组协变

List<CmdbBaseApp> apps = new ArrayList<>();
apps.addAll(new ArrayList<CmdbMiddleResponse>());
apps.addAll(new ArrayList<CmdbProjectResponse>());
parseDomain(apps);


解法二: 泛型实现协变
private void parseDomain(List<? extends CmdbBaseApp> apps) {
}

parseDomain(new ArrayList<CmdbMiddleResponse>());

参考:
https://rumenz.com/java-topic/java/generics/java-generics-what-is-pecs-producer-extends-consumer-super/index.html
https://juejin.cn/post/6911302681583681544