Java与泛型混淆

时间:2013-10-27 02:01:18

标签: java generics

我无法弄清楚使用泛型的正确方法。我有一个类Foo,它有一个通用的(Foo<class>)。然后我想要一张Map<String, Foo>的地图。现在,我可以执行此操作,并将Foo<String>添加为一个地图项,将Foo<Integer>添加为另一个。但是,当我使用map的get方法时,我只返回Foo并且无法再推断出类型,所以如果我这样做:

String s = map.get("StringFoo")

我遇到编译错误,必须这样做:

String s =(String)map.get(“StringFoo”)

做这样的事情有什么好的模式来避免演员,因为这首先是通用的。我或许可以做Map<String, Foo<?>>这样的事情,但是这样做的方法是什么?

我的代码详情如下,可以将其放入目录中,javac *.java && java Main将运行它。

我在Foo.java中有一个通用的java类,

public class Foo<T>
{
    T value;

    public T getValue()
    {
        return this.value;
    }

    public void setValue(T t)
    {
        this.value = t;
    }
}

现在,我在Main.java中有以下测试类:

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

public class Main
{
    public static void main(String[] a)
        {
            Foo<String> fooStr = new Foo<String>();
            fooStr.setValue("TEST 123");

            Foo<Integer> fooInt = new Foo<Integer>();
            fooInt.setValue(314159);

            Map<String, Foo> mapOfFoo = new HashMap<String, Foo>();

            mapOfFoo.put("Strings", fooStr);
            mapOfFoo.put("Integer", fooInt);

            System.out.println("All set");

            String s = mapOfFoo.get("Strings").getValue();

            System.out.println("Got: " + s);

        }
}

编译时,我收到以下错误:

Main.java:21: error: incompatible types
            String s = mapOfFoo.get("Strings").getValue();
                                                       ^
  required: String
  found:    Object
1 error

当我在Main.java中执行此操作时,它可以工作:

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

public class Main
{
    public static void main(String[] a)
        {
            Foo<String> fooStr = new Foo<String>();
            fooStr.setValue("TEST 123");

            Foo<Integer> fooInt = new Foo<Integer>();
            fooInt.setValue(314159);

            Map<String, Foo> mapOfFoo = new HashMap<String, Foo>();

            mapOfFoo.put("Strings", fooStr);
            mapOfFoo.put("Integer", fooInt);

            System.out.println("All set");

            String s = (String)mapOfFoo.get("Strings").getValue();

            System.out.println("Got: " + s);

        }
}

我不确定这样的事情的最佳做法是什么。有没有人有任何建议?

2 个答案:

答案 0 :(得分:4)

您似乎在Map声明和构造中使用了非泛型Foo。取而代之的是:

Map<String, Foo> mapOfFoo = new HashMap<String, Foo>();

应该是这样的:

Map<String, Foo<String>> mapOfFoo = new HashMap<String, Foo<String>>();

然后你不必转换为String - 首先使用泛型的原因之一,而Foo#getValue()将明确地返回一个字符串。

答案 1 :(得分:3)

这种构造在Java中实际上并不常见,问题在于泛型参数是仅编译时的特性,而映射检索是仅运行时的特性,所以没有办法编译器,以了解您的规则,而不是在某处发出警告。

实现这样的结构最安全的方法是通过一个包装类,它在逻辑上保证类型安全,你所要做的就是在getter上抛出SuppressWarnings("unchecked")

public class FooMapper {

  private Map<Class<?>, Foo<?>> fooMap = new HashMap<>();

  public <T> void setFoo(Class<T> clazz, Foo<T> foo) {
    fooMap.put(clazz, foo);
  }

  @SuppressWarnings("unchecked")
  public <T> Foo<T> getFoo(Class<T> clazz) {
    return (Foo<T>) fooMap.get(clazz);
  }

}

这个包装器保证你永远不会使用错误的Foo对象放置错误类型的Class,因此'unchecked'强制转换永远不会失败。