将子列表转换为一行中的父项列表

时间:2015-05-23 07:57:36

标签: java generics inheritance polymorphism java-8

List的{​​{1}}转换为Banana的{​​{1}} ...

List

...在返回之前我必须执行以下两步操作:

Fruit

为避免中间步骤,可以按以下步骤完成:

public class Fruit { }

public class Banana extends Fruit { }

public List<Banana> allBananas() {
    return new ArrayList<>(Arrays.asList(new Banana(), new Banana()));
}

但在这种情况下,我必须创建新的public List<Fruit> getFruits() { List<? extends Fruit> fruits = (List<? extends Fruit>) allBananas(); return (List<Fruit>) fruits; } 对象,这在性能方面不是很好。

我想我不理解某些事情,也许我做错了。 我想知道我的代码完全正确和安全吗?如果是这样 - 是否有更好的投射方式?如果不是 - 什么是错的,可以改进?

3 个答案:

答案 0 :(得分:3)

你可以做到

public List<Fruit> getFruits() {
    return new ArrayList<>(allBananas());
}

请注意,您的转换示例不安全,您将收到编译警告。这是因为List<Banana> List<Fruit>

如果List<Banana> <{1}},您可以将任何 List<Fruit>添加到其中,不该&#39; T:

Fruit

所以将// won't compile, because List<Banana> is not a List<Fruit> List<Fruit> fruit = (List<Fruit>) myBananaList(); // if it did compile (or if you do it with casting), this would "work" fruit.add(new Apple()); // doesn't make sense // but if something uses same Banana list as a Banana list // and gets an Apple instead of a banana, it will cause a class cast exception Banana banana = bananas.get(index); 投射到List<Banana>在技术上总是不正确的,这就是为什么Java不会让你直接这样做的原因。

你对List<Fruit>的中间演员有点误导。在你的情况下,它恰好因the way Java generics are implemented而起作用,但它并不比做这样的事情更好:

List<? extends Fruit>

它编译的原因与下面的代码编译相同,但是这个代码在运行时会失败,因为类型擦除不会适用:

// DON'T DO THIS!
public List<Fruit> getFruits_bad() {
    Object fruits = allBananas();
    return (List<Fruit>) fruits;
}

答案 1 :(得分:3)

除了将方法签名更改为List<? extends Fruit>之外,您还可以使用Collections.unmodifiableList获取List<Fruit>的只读List<Banana>视图:

public List<Fruit> getFruits() {
    List<Banana> bananas = getBananas();
    return Collections.unmodifiableList(bananas);
}

unmodifiableList并将方法声明为List<? extends Fruit>的目标都是相同的目标 - 让此方法的客户端不要将橙色添加到香蕉列表中。

答案 2 :(得分:2)

这实际上取决于你想要达到的目标。

如果您很乐意创建列表的副本,那么我只会使用以下内容:

public List<Fruit> getFruits() {
    return new ArrayList<>(allBananas());
}

现在它独立于原始列表,因此调用者可以随心所欲地执行它。 (如果他们修改任何现有的香蕉,当然会看到这些修改,但这不是对列表本身的更改。)

然而,听起来你不想复制列表,这意味着你有效地希望在现有列表上有某种视图。这里棘手的一点是安全地做到这一点。您的广告投放代码安全:

// Don't do this!
public List<Fruit> getFruits() {
    List<? extends Fruit> fruits = (List<? extends Fruit>) allBananas();
    return (List<Fruit>) fruits;
}

这就是为什么它被打破了:

List<Banana> bananas = allBananas();
List<Fruit> fruit = getFruits();
System.out.println(bananas == fruits); // Same list
fruit.add(new Apple()); // This is fine, right?
Banana banana = bananas.get(0); // This should be fine, right?

您最后会在最后一行中遇到无效的强制转换异常,因为您的getFruits()方法基本上违反了类型安全性。 List<Banana> List<Fruit>

您的安全选择是:

  • 将方法返回类型更改为List<? extends Fruit>,此时您不需要任何转换:

    public List<? extends Fruit> getFruits() {
        return allBananas();
    }
    

    现在调用者无法添加到列表中(null除外),但可以从列表中删除项目。

  • 使用允许添加的List<Fruit>实现,但在执行时检查每个项目是Banana,例如

    return (List<Fruit>) Collections.checkedList(allBananas(), Banana.class);
    
  • 返回列表的不可修改的视图,例如

    public List<Fruit> getFruits() {
        return Collections.unmodifiableList(allBananas());
    }
    

哪个选项合适(包括复制)与上下文有关。