2016-06-22 6 views
8

Я немного смущен о Java lambdas и методе ссылок на методы. . Для бывших, у нас есть этот код:Почему эквивалентное выражение лямбда и ссылка на метод ведут себя по-разному при захвате значения статического поля?

import java.util.function.Consumer; 

public class Main { 

    private static StringBuilder sBuilder = new StringBuilder("1"); 

    public static void main(String[] args) { 
     Consumer<String> consumer = s -> sBuilder.append(s); 
     sBuilder = new StringBuilder("2"); 
     consumer.accept("3"); 
     System.out.println(sBuilder); 
    } 

} 

Выход:

23 

Это работает, как ожидалось, но если заменить

s -> sBuilder.append (s)

с

sB uilder :: добавить

выход будет:

2 

У вас какие-либо идеи, как это объяснить? Это не одно и то же? Благодарю.

+1

ли [это] (http://stackoverflow.com/questions/30514995/what-is-the-difference-between-a-lambda-and-a-method-reference-at-a -продолжительность) слишком изогнуто для дубликата? –

+0

@SotiriosDelimanolis немного :) спасибо за полезные ссылки и ответ – j2esu

ответ

10

В выражении лямбда поле sBuilder фиксируется, но не оценивается. Он будет оцениваться только при вызове соответствующего метода интерфейса функции. В этот момент sBuilder ссылается на новый экземпляр, созданный и назначенный на поле с

sBuilder = new StringBuilder("2"); 

В ссылке методе sBuilder поле вычисляется непосредственно для получения Consumer экземпляра. Это значение ссылается на экземпляр, созданный в статическом инициализаторе

private static StringBuilder sBuilder = new StringBuilder("1"); 

и Consumer будет работать на том. Вы печатаете новый.


Из спецификации языка Java, касающийся Run-Time Evaluation of Method References

Тело метода вызова зависит от формы выражения ссылки метода следующим образом:

Если форма ExpressionName :: [TypeArguments] Identifier или Primary :: [TypeArguments] Identifier, тогда тело метода вызова вызвано выражением вызова метода для comp ile-time декларация, которая является объявлением времени компиляции ссылочным выражением метода. Оценка времени выполнения выражения вызова метода является таким, как определено в §15.12.4.3, §15.12.4.4 и §15.12.4.5, где:

  • Режим вызова является производным от времени компиляции как указано в п. 15.12.3.

  • Целевая ссылка значение ExpressionName или Primary, как определено при использовании метода опорного выражение было оценено .

  • Аргументы выражения вызова метода являются формальными параметрами метода вызова.

+1

Другими словами, 's -> sBuilder.append (s)' эквивалентен 's -> Main.sBuilder.append (s)', в то время как ' sBuilder :: append' эквивалентен 'StringBuilder temp = Main.sBuilder; s -> temp.append (s) ' –

+1

@TavianBarnes Вы можете отредактировать это, если хотите. –

+0

Из любопытства, почему сначала пометить его как сообщество wiki? –