2016-11-26 22 views
5

我正在使用lambda来实现下面的Java程序中的函数接口。当lambda作为参数传递给泛型方法时,编译器会标记“不兼容类型”错误,因为它会推断出lambda表达式实现了接口,该接口将编译器将lambda参数(“thing”)解释为当lambda尝试将它传递给需要类型为Round的参数的方法(testRound)时,它的类型为Shape。这个错误对我来说很有意义。为什么lambda类型推断失败,但成功取得等价的方法引用?

但等效的方法参考不会引发错误消息。我一直在误解,一个lambda和一个可以代替lambda的方法引用是可以互换的。在这里,情况并非如此。

public class Main 
{ 
    public static void main(String... args) 
    { 
     methodB(thing -> Main.testRound(thing)); // incompatible types 
     methodB(Main::testRound);    // no problem here 
    } 

    static <T extends Shape> void methodB(Func<T> function) 
    { 
    } 

    static boolean testRound(Round thing) 
    { 
     return true; 
    } 
} 

interface Func<T> 
{ 
    boolean test(T ob); 
} 

class Shape 
{ 
} 

class Round extends Shape 
{ 
} 

为什么当lambda失败时方法引用成功?

UPDATE

文斯Emigh找到了答案,作为接受的,下面我已标记。虽然这不是我的问题的一部分,这里有四个方法可以解决的事实,拉姆达只是推断为存在Func<Shape>类型,如果一个人真的坚持使用lambda表达式上:

// Use a type witness. 

Main.<Round>methodB(thing -> testRound(thing)); 

// Make the lambda's argument type explicit. 

methodB((Round thing) -> testRound(thing)); 

// Cast the argument. 

methodB(thing -> testRound((Round)thing)); 

// Store the lambda reference in a Func<Round> variable. 

Func<Round> lambda = thing -> testRound(thing); 
methodB(lambda); 

我没有看到任何除非人们认为lambda稍微不密集(也许可读性更强一点),否则就更喜欢这些方法之一。但是,如果你想要他们,他们就在那里。

+1

我可以做出的唯一假设(我仍然觉得它是一个拉伸)是:*推论发生在不同的时间*。对于lambda,'thing'被视为'T extends Shape'(一旦声明'thing'就被推断出来),而'testRound'明确需要'Round',导致错误。当使用方法引用时,参数被视为'Round',因为它是从'Main :: testRound'而不是'(T thing)'推断的。希望这是有道理的,并且记住这是基于经验而不是文档的怀疑论。我正在研究这个问题,希望得到更好的(更正式的)答案 –

+0

@VinceEmigh,我有类似的想法,但有类似的警告。如果您发现任何问题,请告诉我。它确实令人困惑。 –

+0

也许这样:在第一次调用时,lambda实现''Func ''接口,编译器从中推断lambda的参数是'Shape'类型,并且传递'Shape 'testRound'。在第二次调用中,实现的参数类型是来自方法签名的_copied_,这里是'Round thing'。 (见JLS 15.12。3,“编译时参数类型是编译时声明[。]”的形式参数的类型)的确,这意味着其他方面相同的lambda表达式和成员引用是不可互换的。 –

回答

1

JLS §15.13.2

不同于lambda表达式,方法参考可以是全等与通用功能类型(即,具有输入参数的函数的类型)。这是因为lambda表达式需要能够声明类型参数,并且没有语法支持这个;而对于方法引用,则不需要这样的声明。

由于没有指定类型参数,lambda表达式会引发错误。这会导致T被编译为Shape(如您在文章中所述),因为没有任何东西可以帮助推断参数的类型。

至于方法引用,由于可以从方法的参数中推断出类型,因此不需要显式类型参数,如上面JLS语句中所述。

相关问题