通用参数(如果raw默认为Object)

时间:2019-06-20 07:49:00

标签: java generics java-7

我正在研究基于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
    ...
}

2 个答案:

答案 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和实现。