【问题标题】:Type inference strangeness类型推断异常
【发布时间】:2014-03-18 23:29:05
【问题描述】:

所以我有一个Tuple2 类如下:

public final class Tuple2<T1, T2> {
   private final T1 mItem1;
   private final T2 mItem2;

   public T1 getItem1() { return mItem1; }
   public T2 getItem2() { return mItem2; }

   public Tuple2(final T1 pItem1, final T2 pItem2) {
      mItem1 = pItem1;
      mItem2 = pItem2;
   }

   public static <TItem1, TItem2> Tuple2<TItem1, TItem2>
   Create(final TItem1 pItem1, final TItem2 pItem2) {
      return new Tuple2<>(pItem1, pItem2);
   }
}

我正在尝试实例化 ListTuple2s,但类型推断似乎很奇怪。我希望我能做这样的事情:

// doesn't work
// note that Type2 extends Type1, and Type3 extends Type2
final List<Tuple2<String, Class<?>>> list = Arrays.asList(
   Tuple2.Create("1", Type1.class),
   Tuple2.Create("2", Type2.class),
   Tuple2.Create("3", Type3.class)
);

// still doesn't work
final List<Tuple2<String, Class<? extends Type1>>> list = Arrays.asList(
   Tuple2.Create("1", Type1.class),
   Tuple2.Create("2", Type2.class),
   Tuple2.Create("3", Type3.class)
);

如果我转换为 Class&lt;?&gt;Class&lt;? extends Type1&gt;,这些都不起作用。

它需要我做的是:

final List<Tuple2<String, ? extends Class<? extends Type1>>> list = Arrays.asList(
   Tuple2.Create("1", Type1.class),
   Tuple2.Create("2", Type2.class),
   Tuple2.Create("3", Type3.class)
);

但是,如果我想定义对这些Tuple2s 之一的引用,我必须写:

final Tuple2<String, ? extends Class<? extends Type1>> item = list.get(0);

这是一个丑陋的类型名称...有没有办法简化它?为什么它必须是一个Tuple2“扩展Class的东西”,而不是简单的“Class”?

我发现的唯一更简单的方法是使用 Class 原始类型,这似乎不鼓励,需要进行一些转换:

final List<Tuple2<String, Class>> list = Arrays.asList(
   Tuple2.Create("1", (Class)Type1.class),
   Tuple2.Create("2", (Class)Type2.class),
   Tuple2.Create("3", (Class)Type3.class)
);
final Tuple2<String, Class> item = list.get(0);

【问题讨论】:

    标签: java generics types casting type-inference


    【解决方案1】:

    这是老generic invariance再次抬头。

    对于每次调用Create,类型推断为:

    <String, Class<Type1>>
    <String, Class<Type2>>
    <String, Class<Type3>>
    

    您可能知道,List&lt;Dog&gt; 不是List&lt;Animal&gt;,同样的概念也适用于此。 Tuple2&lt;String, Class&lt;Type1&gt;&gt; 不是 Tuple2&lt;String, Class&lt;?&gt;&gt;,而是 Tuple2&lt;String, ? extends Class&lt;?&gt;&gt;

    所以对于推断出的三种Tuple2 类型:

    Tuple2<String, Class<Type1>>
    Tuple2<String, Class<Type2>>
    Tuple2<String, Class<Type3>>
    

    他们的共同类型是Tuple2&lt;String, ? extends Class&lt;? extends Type1&gt;&gt;

    据我所知,解决此问题的唯一方法是为每次调用提供类型见证:

    final List<Tuple2<String, Class<?>>> list = Arrays.asList(
       Tuple2.<String, Class<?>>Create("1", Type1.class),
       Tuple2.<String, Class<?>>Create("2", Type2.class),
       Tuple2.<String, Class<?>>Create("3", Type3.class)
    );
    

    应该可以的。

    这似乎应该工作:

    final List<Tuple2<String, Class<?>>> list = Arrays.asList(
       Tuple2.Create("1", (Class<?>)Type1.class),
       Tuple2.Create("2", (Class<?>)Type2.class),
       Tuple2.Create("3", (Class<?>)Type3.class)
    );
    

    但它不能编译。编译器会捕获转换并将Class&lt;?&gt; 推断为Class&lt;capture of ?&gt;。所以你最终得到的推断类型是这样的:

    <String, Class<capture #1 of ?>>
    <String, Class<capture #2 of ?>>
    <String, Class<capture #3 of ?>>
    

    而且仍然是三种不同的类型。

    老实说:我不知道为什么会这样,只是确实如此。我一直无法找到一个明确的答案。 The spec says this:

    • 如果 Ti 是 ? 形式的通配符类型参数 [...],则 Sifresh 类型变量[...]。

    这似乎暗示capture of ? 被视为一种新类型,与? 不同。

    在我看来,从版本 7 开始,推理与通配符之间存在差距。

    【讨论】:

    • 谢谢,这不像我想要的那样干净,但稍微好一点。 (虽然我使用静态方法来获取类型推断,但如果这是我能得到的最好的,我也可以在这种情况下使用构造函数)。为什么我不能将TypeX.class 转换为Class&lt;?&gt;
    • 查看我的编辑:不幸的是,对于为什么会出现这种情况,我真的没有一个很好的答案,但我给出了我的理解。
    猜你喜欢
    • 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
    相关资源
    最近更新 更多