用于Java中看起来很奇怪的显式类型参数声明语法

时间:2013-03-26 04:47:11

标签: java generics syntax types

我最近遇到了一种奇怪的语法,用于在调用Java方法时显式声明泛型类型。例如:

Collections.<String>emptyList();

返回空List<String>。但是,这似乎很愚蠢,因为<T> emptyList()的实现只是未经检查的类型转换(List<T>) EMPTY_LIST,因此所有结果都具有相同类型的擦除(并且是相同的对象。)此外,这种显式类型通常不需要声明,因为编译器通常可以推断出类型:

List<String> empty = Collections.emptyList();

在做了一些挖掘之后,我发现另外两次你想要使用这种语法,而且它们都是由于使用了Guava库而且显然试图在一行上放置太多语句

  1. 装饰集合,例如使用同步包装器,并且编译器无法推断类型。如果您取出类型声明,则以下内容无效:cannot convert from Set<Object> to Set<String>

    Set<String> set = Collections.synchronizedSet(Sets.<String>newHashSet());
    
  2. 当编译器尝试制作过于具体的类型参数时,获取较少的特定类型参数。例如,如果没有类型声明,则以下语句也会抱怨:cannot convert from Map<String, String> to Map<String, Object>

    Map<String, Object> toJson = ImmutableMap.<String, Object>of("foo", "bar");
    
  3. 我觉得具有讽刺意味的是,在第一种情况下,推断类型参数过于笼统,在第二种情况下它们过于具体,但我认为这只是Java中泛型系统的一个工件。

    然而,除了strange use cases invented by the Guava team之外,这种语言结构本身似乎是可以避免的。此外,对我来说,似乎很明显 是编译器在上述两个示例中推断类型参数的一种方式,而开发人员只是选择不这样做。是否有在Java编程中使用此构造所必需或​​有用的示例,还是仅仅为了使编译器更简单/ JDK开发人员的生活更容易存在?

3 个答案:

答案 0 :(得分:5)

“关闭编译器”如何“不必要或有用?”我觉得必须对我的代码编译很有用。

有些时候无法推断出正确的类型,正如您已经发现的那样。在这种情况下,有必要explicitly specify the type parameters。编译器的一些例子不够智能:

如果确实想要深入研究类型推断的复杂性,那么它将以Java语言规范开始和结束。您需要关注JLS §15.12.2.7. Inferring Type Arguments Based on Actual Arguments§15.12.2.8. Inferring Unresolved Type Arguments

答案 1 :(得分:1)

我发现至少有一种情况,编译器正确地推断出类型,并且仍然需要它:当你想将结果用作更通用的类型时。采用这种方法,它基本上从零个或多个List<T>个对象创建T

public static <T> List<T> listOf(T... items) {
    ArrayList<T> list = new ArrayList<T>();
    for (T item : items)
        list.add(item);

    return list;
}

这个想法是你可以像这样使用它:

List<Integer> numbers = ListUtils.listOf(1, 2, 3);

现在,假设您有一个可以接收List<Object>的方法:

public static void a(List<Object> objs) {
    ...
}

并且您希望提供通过listOf()方法构建的列表:

a(ListUtils.listOf(1, 2, 3));

这将无法编译,因为方法参数类型为List<Object>且提供的参数为List<Integer>。在这种情况下,我们可以将调用更改为:

a(ListUtils.<Object>listOf(1, 2, 3));

按预期编译。

答案 2 :(得分:0)

Java类型推断非常弱。只有在方法的结果定义变量时,才有必要在emptyList()这样的泛型方法中包含显式类型。如果你试图传递一个空列表作为另一个方法的参数(例1),这是我每天出现的情况(我还没有使用Guava),编译器就完全放弃了类型推断。我没有看到如何将空列表声明为本地的单用变量是“在一行上放置太多语句”,就像你所说的那样;空列表是一个非常简单的子表达式,除了Java的悲惨类型推断使它变得复杂。与Scala比较,它将在3种不同的情况下进行推理。