了解泛型以及如何避免强制转换

时间:2014-01-05 14:52:10

标签: java generics generic-collections

以下代码是我希望实现的一个例子。不幸的是,如果没有函数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
    {
    }
}

3 个答案:

答案 0 :(得分:3)

在这种情况下,Map是从Class<? extends E>Collection<? extends E>的映射。就编译器而言,任何Class<? extends E>都可以合法地映射到任何Collection<? extends E>

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

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

答案 1 :(得分:2)

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

使用泛型时,大多数情况下无法摆脱强制转换。但是,大多数这些强制转换是未经检查的转换,由于类型擦除而在运行时被删除。关键是,当可以保证类型安全时,编译器会让你独自一人,你的类的客户端会得到一个类型安全的,无需转换的接口。

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

答案 2 :(得分:-2)

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