【问题标题】:Understanding generics and how to avoid cast了解泛型以及如何避免强制转换
【发布时间】:2014-01-05 14:52:10
【问题描述】:

以下代码是我希望实现的示例。不幸的是,如果没有函数 getElements() 的强制转换,就无法编译此代码。

我仍然完全理解 Java 泛型,但这似乎是一个相当简单的案例。

任何帮助将不胜感激。

import org.junit.Test;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

public class GenericFruitTest
{
    @Test
    public void genericFruit()
    {
        Collection<Apple> apples = new FruitBasket().getApples();
        Collection<Orange> oranges = new FruitBasket().getOranges();

        Collection<Rye> rye = new BreadBasket().getRye();
    }

    class FruitBasket extends Basket<Fruit>
    {
        Collection<Apple> getApples()
        {
            return getElements(Apple.class);
        }

        Collection<Orange> getOranges()
        {
            return getElements(Orange.class);
        }
    }

    private class BreadBasket extends Basket<Bread>
    {
        private Collection<Rye> rye;

        public Collection<Rye> getRye()
        {
            return getElements(Rye.class);
        }
    }

    class Basket<E>
    {

        Map<Class<? extends E>, Collection<? extends E>> classCollectionMap = new HashMap<Class<? extends E>, Collection<? extends E>>();

        <T extends E> Collection<T> getElements(Class<T> elementClass)
        {
            // The following line throws compile time "incompatible types" without cast
            return classCollectionMap.get(elementClass);
        }

    }

    class Rye extends Bread
    {
    }

    class Apple extends Fruit
    {
    }

    class Orange extends Fruit
    {
    }

    class Fruit
    {
    }

    private class Bread
    {
    }
}

【问题讨论】:

  • 这是意料之中的事,因为编译器无法保证 Collection 确实存储在地图中,在 Orange.class 键下。它可以是 Collection 或 Collection。使用 Basket 唯一可以保证的就是篮子里有水果。
  • 有没有办法使用泛型来保证返回的集合中可以预期什么类型的水果?

标签: java generics generic-collections


【解决方案1】:

本例中的Map 是从Class&lt;? extends E&gt; 映射到Collection&lt;? extends E&gt;。就编译器而言,任何Class&lt;? extends E&gt; 都可以合法地映射到任何Collection&lt;? extends E&gt;

您碰巧总是将与集合匹配的类放入 Map 中这一事实编译器并不知道 - 因为您可以将任何扩展 E 的类放入映射中。

不幸的是,在这种情况下您将需要强制转换 - 尽管至少泛型允许您将强制转换放入方法中,因此使用该方法的任何人都看不到它。

【讨论】:

    【解决方案2】:

    它需要强制转换,因为编译器不知道任何给定键所具有的集合类型。你的地图说它会将一个未知类型的Class映射到一个未知类型的Collection,但是你无法告诉地图它们必须是同一类型。

    在使用泛型时,大多数时候您无法摆脱强制转换。然而,这些转换中的大多数是未经检查的转换,由于类型擦除,它们会在运行时被删除。关键是,当可以保证类型安全时,编译器会让你一个人呆着,你的类的客户端会得到一个类型安全、无强制转换的接口。

    当然,如果你搞砸了,运行时没有办法提醒你。

    【讨论】:

      【解决方案3】:

      就像 C++ 中的模板元编程一样,必须指定特定的类型,否则编译器不知道如何在运行时运行。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-12-29
        • 1970-01-01
        • 2012-03-18
        相关资源
        最近更新 更多