2011-04-05 47 views
4

我最近在重构代码时遇到了这个问题:参数化方法如何在某些情况下隐式约束而不是其他情况?

下面的方法“getList()”有一个参数化的返回类型。在此之下,我已经提出了三种尝试隐含地将<T>绑定到<Integer>的方法。

我不明白为什么前两个编译和运行正确,而第三个(bindViaMethodInvocation)甚至不会编译。

任何线索?

在寻找StackOverflow的类似问题时,我遇到了这个问题: Inferred wildcard generics in return type。答案在那里(信用Laurence Gonsalves)有几个有用的参考链接来解释应该怎么做: “这里的问题(如你所建议的)是编译器执行Capture Conversion。我相信这是一个结果JLS的§15.12.2.6 of the JLS。“或者赋值List<Integer>或从返回List<Integer>方法返回语句 -

package stackoverflow; 

import java.util.*; 

public class ParameterizedReturn 
{ 
    // Parameterized method 
    public static <T extends Object> List<T> getList() 
    { 
     return new ArrayList<T>(); 
    } 

    public static List<Integer> bindViaReturnStatement() 
    { 
     return getList(); 
    } 

    public static List<Integer> bindViaVariableAssignment() 
    { 
     List<Integer> intList = getList(); 
     return intList; 
    } 

    public static List<Integer> bindViaMethodInvocation() 
    { 
     // Compile error here 
     return echo(getList()); 
    } 

    public static List<Integer> echo(List<Integer> intList) 
    { 
     return intList; 
    } 
} 
+0

谢谢马特!我想我没有正确地对这些做减价。 – 2011-04-05 14:40:00

回答

7

前两种方法中的上下文受赋值转换使用getList()。同样的不是对于bindViaMethodInvocation为真 - 使用表达式作为方法参数是而不是受分配转换的影响。

JLS section 15.12.2.8

如果任何方法的类型参数没有从类型的实际参数推断出,他们现在推测如下。

  • 如果方法结果发生在将被赋值转换(第5.2节)为类型S的上下文中,则令R为该方法的声明结果类型,并设R'= R [ T1 = B(T1)... Tn = B(Tn)]其中B(Ti)是前一节中Ti推断出的类型,或Ti如果没有推断出类型。

JLS不清楚为什么返回语句计数在这里。我能找到的最接近的是14.17

带表达式的return语句必须包含在该声明为返回(8.4节),或发生编译时错误值的方法声明。表达式必须表示某个类型T的变量或值,否则会发生编译时错误。类型T必须是可分配的(第5.2节)到方法的声明结果类型,否则会发生编译时错误。

(如果第5.2节指出,return语句受到分配转换这将是很好。)

0

JLS 3#15.12.2.8允许类型推断在有限的环境中。我将它评为设计错误。表达式的含义应该是上下文无关的,对每个人来说都更容易。

由于getList()的含义因周围环境而异,这对于Java程序员来说是违反直觉的(前所未有),所以你会发现前两个编译器和第三个编译器都没有。你并不孤单,这种问题已经反复提出。他们可以告诉我们RTFS,但需要阅读规格的人越多,规格就越差。

当然,如果上下文相关的解释真的有用并且需要,我们必须是实用的。但几乎没有证据支持这一点。这种类型推断是危险的,大多数使用它的代码都是99%错误设计的。目前尚不清楚他们认为有必要添加这种类型推断规则。

如果Java泛型被“定义”,即T的值在运行时可用于方法调用,我们可以想象这种类型推断是安全和有用的。但是,T在运行时不可用,因此getList()调用是上下文无关的,因此无法返回调用站点预期的正确类型。除非有一些保护类型健全性的超文本应用程序逻辑。然后它几乎不是“静态打字”。

已经有很多人走得更远,并要求以下类型的推理:“如果我写的,我当然知道运行时返回类型是酒吧,因此停止质疑我,愚蠢的编译器”

Object getFoo(){ .. } 

Bar bar = getFoo(); 

因为

我尊重那个意见,但你应该选择一种不同的语言。在静态和动态类型中,程序员都知道类型,但是静态类型的要点在于,我们希望明确地写下源代码中的类型 - 不是为了帮助编译器,而是为了让自己受益。 “类型推断”违反了确切的目标;它只应该被做,如果它不给任何人读任何类型的真正的代码的任何神话。不幸的是,Java的类型推断非常神秘。

相关问题