输入推理陌生感

时间:2014-03-18 23:29:06

标签: java generics types casting type-inference

所以我有一个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);
   }
}

我试图实例化List Tuple2,但类型推断似乎很奇怪。我希望我能做到这样的事情:

// 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<?>Class<? extends Type1>,这些都不起作用。

它要求我做的是:

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)
);

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

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

这是一个丑陋的名字......有没有办法简化这个?为什么它必须是Tuple2&#34;延伸Class扩展Type1&#34;的东西,而不仅仅是{{1}某事&#34;?

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

Class

1 个答案:

答案 0 :(得分:1)

这是旧的generic invariance再次抬头。

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

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

如您所知,List<Dog>不是List<Animal>,同样的概念适用于此。 Tuple2<String, Class<Type1>>不是Tuple2<String, Class<?>>,而是Tuple2<String, ? extends Class<?>>

因此对于三种推断的Tuple2类型:

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

他们的共同点是Tuple2<String, ? extends Class<? extends Type1>>

据我所知,解决这个问题的唯一方法是为每次通话提供一个类型见证:

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<?>推断为Class<capture of ?>。因此,您最终得出的推断类型如下:

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

还有三种截然不同的类型。

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

  
      
  • 如果T i 是形式为?的通配符类型参数[...],那么S i fresh 类型变量[...]。
  •   

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

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