【问题标题】:Return a Class instance with its generic type返回具有泛型类型的 Class 实例
【发布时间】:2015-04-15 20:58:59
【问题描述】:

这是一个简单的示例,演示了我遇到的与类型擦除相关的问题。我有这样的课:

public abstract class AbstractHandler<T> {

    ...
    public abstract Class<T> handledType();
}

然后我有这个实现:

public class ConcreteHandler extends AbstractHandler<Map<String, List<Thing>>> {

    @Override
    public Class<Map<String, List<Thing>>> handledType() {
        //what do I return here?!
    }
}

我无法返回Map&lt;String, List&lt;Thing&gt;&gt;.class,因为这在语法上甚至都无效。我尝试将子类型中的泛型类型参数设为HashMap&lt;String, List&lt;Thing&gt;&gt;,然后返回new HashMap&lt;String, List&lt;Thing&gt;&gt;().getClass(),但这不起作用,因为Class&lt;T&gt;#getClass() 的返回类型是Class&lt;? extends T&gt;。我从 Guava 中查看了 TypeTokengetRawType 方法看起来很有希望,但它返回 Class&lt;? super T&gt;

我暂时有一个解决方法,如下所示:

public class ThingListMap {
    private Map<String, List<Thing>> thingListMap;

    ...
}

我只是使用ThingListMap 作为通用类型参数。

另一种可能的解决方法是执行强制转换:

public Class<Map<String, List<Thing>>> handledType() {
    return (Class<Map<String, List<Thing>>>) new HashMap<String, List<Thing>>().getClass();
}

有没有更优雅的方式来做到这一点?

编辑:针对其中一个答案,我无法更改 handledType 方法的签名,因为我不拥有或控制其来源。

【问题讨论】:

  • 需要更多关于该方法的目的的信息
  • 有没有办法做到这一点?" 在纯 Java 中,没有。
  • @eduyayo 这更像是一个概念性问题。我确实有一个解决方法,但我想看看是否有更优雅的方法。
  • 你的课设计得不好。 java.lang.Class 类的实例代表类(废话),但您正试图使用​​一个来代表 type,这是更通用的东西。 Java 确实有一个用于此概念的 Type 类,但它可能也无法满足您的目的。
  • @JohnBollinger 我完全同意,但我没有设计这个类,所以我不能改变它。 :(

标签: java generics type-erasure generic-type-argument


【解决方案1】:

出于某种原因,Java 不允许您将 Map.class 直接转换为 Class&lt;Map&lt;String, List&lt;Thing&gt;&gt;&gt;。无论如何,这是一个未经检查的演员表。

但是投两次是合法的,首先是Class&lt;?&gt;,然后是Class&lt;Map&lt;String, List&lt;Thing&gt;&gt;&gt;

return (Class<Map<String, List<Thing>>>) (Class<?>) Map.class;

作为未经检查的演员,您可能需要添加@SuppressWarnings("unchecked")

【讨论】:

  • @eduyayo 我不明白你的评论或它如何适用于这个答案。
  • “出于某种原因 [...]” 基本上与您不能将 List&lt;Cat&gt; 转换为 List&lt;Dog&gt; 的原因相同。两者都不是另一个的超类型或子类型。另见"Cannot convert from List<List> to List<List<?>>"。 RE:这个答案在这里,铸造对于对象创建之类的东西很好,但请注意,例如,情况就是这样。 Class&lt;Map&lt;String, String&gt;&gt;== Class&lt;Map&lt;Integer, Integer&gt;&gt;.
  • 这是我对临时HashMap&lt;K, V&gt; 实例类的强制强制转换的更好(主观,我猜:p)变​​体。我喜欢不必创建临时实例这一事实。
  • @Radiodef 是的,这就是原因。感谢您指出这一点。
  • @VivinPaliath 另外,这更接近真实类型,因为您返回的是HashMap.class 而不是Map.class
【解决方案2】:

Guava 的方法是使用TypeTokens。你的班级会变成

public abstract class AbstractHandler<T> {
    public TypeToken<T> handledType();
}

public class ConcreteHandler extends AbstractHandler<Map<String, List<Thing>>> {
    @Override
    public TypeToken<Map<String, List<Thing>>> handledType() {
        return new TypeToken<Map<String, List<Thing>>>() {};
    }
}

【讨论】:

  • 是的,那将是理想的。不幸的是,我无法在AbstractHandler&lt;T&gt; 中更改handledType 的签名。我会在问题中提到这一点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多