Java继承场景下泛型与集合返回值类型不兼容问题咨询
Java泛型继承编译异常原因及解决方案
原因解析
1. 为什么getGreeting可以正常编译
普通Java类继承遵循多态规则:Hello实现了Greeting接口,属于Greeting的子类型,因此Hello实例可以直接赋值给Greeting类型的引用,属于Java基础多态的标准表现,不存在问题。
2. 为什么getGreetings编译失败
Java泛型是不变的,不存在继承关系:即使Hello是Greeting的子类型,ArrayList<Hello>也不属于ArrayList<Greeting>的子类型。
这个设计是为了保证类型安全:假设允许ArrayList<Hello>赋值给ArrayList<Greeting>,后续就可以向该列表中加入任意实现Greeting接口的类实例(比如另一个Hi类的对象),会破坏原ArrayList<Hello>的元素类型约束,运行时会出现类型转换异常。
3. 为什么getGenericGreeting编译失败
泛型方法的类型参数O是由调用方决定的,不是方法内部决定的。比如调用方可以写HelloWorld.<Hi>getGenericGreeting(),此时O被指定为实现了Greeting的Hi类,但方法内部返回的是固定的Hello实例,显然不满足类型匹配要求,因此编译器直接阻断这种写法。
可行替代方案
针对getGreetings的方案
- 方案1:使用通配符上界声明返回类型
如果只需要读取返回列表的元素,不需要向列表新增元素,可以修改返回类型为带上界通配符的泛型:public static ArrayList<? extends Greeting> getGreetings() { return new ArrayList<Hello>(); } - 方案2:直接创建
Greeting类型的泛型列表
如果需要对返回列表做读写操作,直接声明列表的泛型为Greeting,加入Hello实例即可:public static ArrayList<Greeting> getGreetings() { ArrayList<Greeting> list = new ArrayList<>(); list.add(new Hello()); return list; }
针对getGenericGreeting的方案
- 方案1:移除不必要的泛型声明,直接返回
Greeting类型
如果不需要根据调用方动态返回不同子类型的实例,直接用普通多态写法即可:public static Greeting getGenericGreeting() { return new Hello(); } - 方案2:由调用方传入实例生成逻辑
如果确实需要保留泛型动态返回的能力,让调用方传入对应类型的构造逻辑:public static <O extends Greeting> O getGenericGreeting(Supplier<O> supplier) { return supplier.get(); } // 调用示例:getGenericGreeting(Hello::new) - 方案3:传入类型Class参数做强制转换
也可以由调用方传入目标类型的Class对象,内部做类型转换,该方案不推荐,类型不匹配时会抛出运行时异常:public static <O extends Greeting> O getGenericGreeting(Class<O> clazz) { return clazz.cast(new Hello()); }
内容的提问来源于stack exchange,提问作者Evan Belcher
相关产品推荐
相关产品推荐

