【问题标题】:Java: Generic types in a type tokenJava:类型标记中的泛型类型
【发布时间】:2012-03-02 05:43:11
【问题描述】:

我有一个接受类型标记的类,然后生成由该类型参数化的类型的对象(好吧,它比这更复杂一点,但这是一个简洁的例子):

public class Test {

    public static void main(String[] args) throws Exception {
        Holder<HashSet<Integer>> i = newObjectHolder(HashSet.class);  // this fails
    }

    static class Holder<T> {
        public Holder(T newInstance) {}
    }

    public static <T> Holder<T> newObjectHolder(Class<T> typeToken) throws Exception {
        Constructor<T> ctor = typeToken.getConstructor();
        return new Holder<T>(ctor.newInstance());
    }
}

如果传递非泛型类型,这可以正常工作,例如:

Holder<Integer> i = Test.newObjectHolder(Integer.class);

但是,如果传递的类型标记是通用的,则它不起作用,如上面所示的行:

Holder<HashSet<Integer>> i = Test.newObjectHolder(HashSet.class);  // this fails

我遇到了问题,但有解决方案吗?如果不降低安全性,我可以在 newObject 的代码中添加 @SuppressWarnings("unused")。直观地说,似乎 newObject 应该能够进行有效的转换,我们知道一个已擦除泛型类型的“新”对象与任何其他对象相同,并且我们没有在方法中使用 T .

【问题讨论】:

  • 这是一个错误,是在我的“简洁”阶段从真实代码中引入的。让我解决它。
  • 已修复。请注意,它仍然不起作用。
  • Foo.newObject的调用应该是对Foo.newObjectHolder的调用吗?
  • 是的,我的示例在早期编辑以解决 Nishant 提出的问题后不一致。我现在用sscce.org 更新了它

标签: java generics types


【解决方案1】:

所以,不好意思,回答我自己的问题...

我找不到任何使用Class&lt;?&gt; 的方法,但是这种称为super 类型标记的神奇技术似乎效果很好。这是更新的代码:

public class Test {

    static class A {}
    static class B extends A {}


    public static void main(String[] args) throws Exception {
        Holder<HashSet<Integer>> i = newObjectHolder(new TypeReference<HashSet<Integer>>() {});  // works
        Holder<A> j = newObjectHolder(new TypeReference<A>() {});  // works
        Holder<A> k = newObjectHolder(new TypeReference<B>() {});  // works
        Holder<B> l = newObjectHolder(new TypeReference<A>() {});  // doesn't compile (good)
    }

    static class Holder<T> {
        public Holder(T newInstance) {}
        T get() { return null; }
    }

    public static <T,U extends TypeReference<? extends T>> Holder<T> newObjectHolder(U typeToken) throws Exception {
        T obj = typeToken.newInstance();
        return new Holder<T>(obj);
    }
}

TypeReference 代码是 here,尽管我怀疑 Guice 的 TypeLiteral 也可以正常工作(但它没有 newInstance 代码,您必须实现它)。

【讨论】:

  • 是的,我一看到问题标题就立刻想到了超类型标记技术。
  • 回答你自己的问题很棒!不要忘记接受您的回答。
  • @Taymon - 我很高兴我能够回答它,是的,但我感到内疚,因为这基本上意味着我在转向 SO 之前可能没有做足够的实验或挖掘。通常我不是通过询问来从 SO 那里得到答案,而是通过找到一个现有的问题(这些天它们经常出现在 Google 搜索结果的顶部),所以也许在这里有这个问题会对其他人有所帮助!
  • 你不应该感到内疚。您的回答仍然对其他人有所帮助。
  • 作为后续,jackson 使用此技术来传递有关要 [de] 序列化的泛型类型的信息,like this
【解决方案2】:

在您现在更新的代码中,您仍然可以

 Holder<HashSet> i = newObjectHolder(HashSet.class);

但不能

 Holder<HashSet<Integer>> i = newObjectHolder(HashSet.class);

旧答案:

我不确定你遇到了什么问题。此代码工作正常。

public class Test {

    public static void main(String[] args) throws Exception {
        @SuppressWarnings("unchecked")
        HashSet<Integer> i = newObject(HashSet.class);
        i.add(new Integer(42));
        i.add(new Integer(666));
        System.out.println(i.size() +":" + Arrays.toString(i.toArray()));
    }

    public static <T> T newObject(Class<T> typeToken) throws Exception {
        Constructor<T> ctor = typeToken.getConstructor();
        return ctor.newInstance();
    }

}

打印

2:[666, 42]

我错过了什么吗?

【讨论】:

  • 你说得对,我的简化太过分了(尽管上面的代码有一个未经检查的警告)。让我解决这个问题。
  • 是的,根据我对泛型的了解,我认为这是不可避免的。
  • 对不起,我修好了。基于您的示例(调用 newObjectHolder)的错误现在是:类型不匹配:无法从 Test.Holder 转换为 Test.Holder>
  • 是的,我仍然无法保持我的示例与现实生活之间的一致性。我想做的是: Holder> i...
  • 最终我需要调用 Holder 上的方法,这些方法应该返回 HashSet&lt;Integer&gt; 而不仅仅是 HashSet
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多