我正在研究基于Java 7的项目。我想在其中一个类上引入一个通用参数,以便可以逐步消除使代码正常工作所需的类强制转换。
让我们介绍一个与我正在研究的类相似的类:
public class A {
public List<B> getB() { ... }
}
B
有很多子类,使用时需要强制转换,这显然不理想。我要进行的修改是这样的:
public class A<T extends B> {
public List<T> getB() {...}
}
在某些情况下,这可以消除所需的铸造。但是,A
被用在项目的很大一部分中,这使得遍历和重写所使用的每种情况都不太有效。
我希望使用原始的A
类可以使getB()
返回B
类型。现实情况是,如果使用raw,getB()
将返回Object
类型。是与Java 7相关的行为,还是我忽略了某些东西?当getB()
原始时,是否有办法使B
返回A
类型?在Java 8中,我似乎也没有遇到过这个问题,尽管我也没有真正在结构如此差的项目中工作。
编辑:在评论中,要求我提供发生问题的具体代码示例。假设以上课程(稍作修改以更好地适合我的实际情况):
A a = new A();
for(B b: a.getB()) { // Compiler error, because `getB()` returns a list of `Object`s instead of `B`s.
...
}
A<B> a = new A<B>();
for(B b: a.getB()) { // Everything is fine here obviously
...
}
答案 0 :(得分:1)
问题在于,如果您使用原始类型,则也会从List
中删除该类型,并且会从List
返回一个原始getB()
类型。由于forEach
循环不允许未经检查的类型转换,因此会出现编译错误。使用免责声明raw types are best avoided,在您的情况下,一种简单的解决方法是使用临时变量进行类型转换:
A a = new A();
List<B> list = a.getB();
for( B b : list ) {
System.out.println(b);
}
或转到emulate the enhanced for loop,在这种情况下,循环头中将允许未经检查的类型转换:
for (Iterator<B> i = a.getB().iterator(); i.hasNext();) {
System.out.println(i.next());
}
答案 1 :(得分:1)
您的意图似乎是,假设一个class C extends B
,您可能有一个A<C>
,其getB()
方法返回了List<C>
。但是,如果允许旧代码将同一个对象的方法视为返回List<B>
的对象,则可以在其上调用getB().add(new B())
,这将破坏整个类型的安全性。
当然,可能是旧代码从来没有做到过,也许列表是不可变的,但是编译器的类型系统不知道。
由于您仍然必须触摸代码,因此要从新的通用类型中受益,您可以扩展该类,例如
public class A {
public List<B> getB() { ... }
}
public class ExtendedA<T extends B> extends A {
public List<B> getB() {
return Collections.unmodifiableList(getT());
}
public List<T> getT() { ... }
}
只有在创建T
并在整个代码路径中一致传递时,才能为新代码或更新代码使用新类型参数ExtendedA
的好处。但是,在ExtendedA
传递给旧代码的任何时候,只要旧代码遵守上述限制,它就会像以前一样继续工作。在不知道实际类型参数的情况下,代码不得尝试添加B
实例。但是在此解决方案中,unmodifiableList
包装程序在运行时强制执行了限制,因此保留了通用类型安全性,这就是为什么包装程序方法的签名允许类型从List<T>
转换为List<B>
的原因。 A
。
最好的迁移策略是尽快适应A
s的所有创建站点,然后ExtendedA
类可能会变得抽象,仅用作旧代码的接口,而{ {1}}是所有新代码和更新代码的实际类型安全API和实现。