将参数化类转换为参数的子类

时间:2014-08-25 19:56:14

标签: java generics casting

有没有办法将一些超类型的列表强制转换为子类型列表?看起来这应该是可能的,因为,如果您创建封装通用列表的非泛型类型,则可以按预期进行转换。例如:

class Foo {}
class Bar extends Foo {}

public class MyClass {
    public static void main(String[] args) {
        ArrayList<Foo> fooList = new ArrayList<>();
        fooList.add(new Bar());

        // No can do. "Cannot cast from ArrayList<Foo> to ArrayList<Bar>"
        // ArrayList<Bar> barList = (ArrayList<Bar>) fooList;
        // Bar bar = barList.get(0);

        FooList newAndImproveFooList = new FooList();
        newAndImproveFooList.add(new Bar());
        BarList barList = (BarList) newAndImproveFooList;
        barList.get(0);
    }
}

class FooList {
    private ArrayList<Foo> list = new ArrayList<>();

    // Simple delegates
    public void add(Foo foo) { list.add(foo); }
    public Foo get(int index) { return list.get(index); }
}

class BarList extends FooList {
    public Bar get(int index) { return (Bar) super.get(index); }
}

这段代码的重点是要表明这个铸造问题可以“轻松”解决。当然,我们可以投射不同类型的数组(因此,FooList可能包含Foo的{​​{1}}不应该是一个问题。

似乎这个问题的唯一解决方案是不使用泛型,但是你必须为你需要列出的每个类型创建一个这个列表的实例,这是泛型的问题本打算解决。

我们可以对列表进行每次访问,但是程序的当前布局(并且不能轻易地改变它)将导致成千上万的演员阵容,就像我们可以投射列表一样,我们只需要一个几个地方的几个演员。

我们无法复制列表,因为它在各处修改。

2 个答案:

答案 0 :(得分:1)

最简单的方法是做一些等同于

的事情
ArrayList<Bar> barList = (ArrayList<Bar>) (ArrayList<? extends Foo>) fooList;

这会抛出一堆关于未经检查的强制转换的编译器警告。编译器试图警告你,没有什么好处可以来。通过执行此演员,如果您要将ClassCastException的元素用作barList个对象,则会丢失所有类型信息并为Bar设置自己。

您可以将Bar项添加到barList,但由于它们是Bar个对象,因此无法将它们检索为Foo个对象。

执行演员表是合法的,就像

一样
Integer value = (Integer) (Object) "I'm not an Integer.";

但这并不意味着它是安全的。 明智地使用它。

答案 1 :(得分:0)

泛型的部分原因是你没有施放。如果您正确使用泛型,则可以静态地确定所有涉及的类型(足够紧密)。

在您的特定情况下,您无法将List<Foo>转换为List<Bar>,因为它可能包含Foo s Bar s,无论是在演员表时或者稍晚些时候。同样,您无法将List<Bar>转换为List<Foo>,因为这样就可以添加不是Foo的{​​{1}}。类型参数不是特定于对象的当前状态;相反,它是对象的类型的一个方面,除其他外,影响所有可能的现在和未来状态的空间。

你是正确的,你可以通过使用裸类型而不是参数化类型来完全避免类型安全检查。如果参数化的方式超出了它的帮助范围,那么也许这就是你应该做的,但这是代码脆弱的一个标志(因为你的代码依赖于无法静态检查的假设)。

你可以通过遍历裸中间类型在不兼容的参数化类型之间进行转换来打败类型检查(以牺牲一些警告为代价),但这绝不意味着你应该这样做,Java也不应该接受直接转换不兼容的类型之间。

至于遗留代码,您可以使用类型边界和协变返回类型来至少取得一些进展:

Bar

您描述的整个设计令人不舒服,但是,超类具有暗示其子类知识的方法,即使它没有实际的编译时依赖性。此外,abstract class Thing { public List<? extends Thing> getAgentList() { return Collections.<Thing>emptyList(); } public List<? extends Thing> getHouseholdList() { return Collections.<Thing>emptyList(); } } class Agent extends Thing { public List<Agent> getAgentList() { // ... } } class Household extends Thing { public List<Household> getHouseholdList() { // ... } } 似乎不太可能有方法Household(例如)。但是,只要你打算这样做,你也可以一路走下去:

getAgentList()

就个人而言,只要您正在编写此代码,我就会考虑借此机会将整个混乱重构为一些不那么复杂的东西。