2016-11-26 3 views
5

Я использую лямбду для реализации функционального интерфейса в программе Java ниже. Когда лямбда передается в качестве аргумента в общий метод, компилятор помещает ошибку «несовместимых типов», поскольку он указывает, что лямбда реализует интерфейс Func <Shape>, в котором компилятор интерпретирует параметр лямбда («вещь») как типа типа Shape, когда лямбда пытается передать его методу (testRound), для которого требуется аргумент типа Round. Эта ошибка имеет смысл для меня.Почему ошибка ввода типа для лямбда, но для эквивалентной ссылки метода?

Но ссылка на эквивалентный метод не вызывает сообщения об ошибке. Я был за неправильным пониманием того, что лямбда и ссылка на метод, которые могли бы заменить эту лямбду, были взаимозаменяемыми. Вот, это не так.

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 
{ 
} 

Почему метод ссылается на успех, когда лямбда не срабатывает?

UPDATE

Vince Emigh нашел ответ, который я помеченный как принято, ниже. Хотя это не часть моего вопроса, вот четыре способа обойти тот факт, что лямбда только выведенная в качестве существ типа Func<Shape>, если один действительно застряло на использование лямбды:

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

Я не вижу повод предпочесть один из них по ссылке метода, если только не чувствуете, что лямбды немного менее плотные (и, может быть, немного более читаемы). Но они там, если вы хотите их.

+1

Единственное допущение, которое я могу сделать (которое я все еще чувствую, это растяжка): * вывод происходит в разное время *. С лямбдой 'вещь' рассматривается как' T extends Shape' (выдается, как только объявляется 'thing'), в то время как' testRound' явно требует 'Round', вызывая ошибку. При использовании ссылки на метод аргумент рассматривается как «Круглый», поскольку он выводится из «Main :: testRound», а не «(T thing)». Надеюсь, это имеет смысл, и имейте в виду, что это скептицизм, основанный на опыте, а не на документации. Я смотрю на это в надежде на лучший (более официальный) ответ –

+0

@VinceEmigh, у меня были несколько похожие мысли, с аналогичными предостережениями. Пожалуйста, дайте мне знать, если вы найдете что-нибудь. Это наверняка озадачивает. –

+0

Возможно, это: В первом вызове лямбда реализует интерфейс 'Func ', из которого компилятор сообщает, что аргумент лямбда имеет тип 'Shape', и это ошибка времени компиляции для передачи 'Shape 'to' testRound'. Во втором вызове тип аргумента для реализации - _copied_ из подписи метода, который здесь является «Round thing». (См. JLS 15.12.3, «типы параметров времени компиляции - это типы формальных параметров объявления времени компиляции [.]»). Действительно, это означает, что в противном случае идентичные lambdas и ссылки на члены не являются взаимозаменяемыми. –

ответ

1

От JLS §15.13.2:

В отличие от лямбда-выражения, эталонный метод может быть конгруэнтны с общим типом функции (то есть, тип функции, которая имеет параметры типа). Это связано с тем, что выражение лямбда должно быть способно объявлять параметры типа, и ни один синтаксис не поддерживает это; в то время как для ссылки на методику такое объявление не требуется.

Выражение лямбда вызывает ошибку, поскольку аргумент типа не указан. Это приводит к тому, что T будет скомпилирован как Shape (как упоминалось в вашем сообщении), так как нет ничего, чтобы помочь вывести тип аргумента.

Что касается ссылок на методы, поскольку тип может быть выведен из параметров метода, не требуется аргумент явного типа, как указано в вышеприведенном выше описании JLS.