2015-02-16 3 views
1

Java 8 позволяет использовать одно и то же лямбда-выражение в разных контекстах. Рассмотрим следующий код, который демонстрирует это:Повторное использование лямбда-выражений в Java 8

public class LambdasTypeCheck { 
     public static void main(String []args) { 
      //Same lambda expression can be used in different contexts. 
      Predicate<List<String>> predicate = (l) -> l.add("1"); 
      Consumer<List<String>> consumer = (l) -> l.add("1"); 
     } 
    } 

Выражение лямбда (l) -> l.add("1"); дублируется в двух местах. Как я могу избежать этого дублирования? Один из способов повторного использования этого выражения лямбда заключается в том, что есть некоторый интерфейс/класс, который является родителем всех лямбда-выражений, так что я могу назначить любое лямбда-выражение ссылке такого интерфейса/класса.

+2

У вас есть сценарий реальной жизни, в котором одно и то же лямбда-выражение может быть назначено различным функциональным интерфейсам? Пример, который вы дали, имеет мало смысла. '(l) -> l.add (" 1 ")' не следует использовать в качестве предиката. – Eran

+0

На данный момент я этого не делаю. Означает ли это, что мне не должно быть любопытно? Я хочу знать, можно ли это сделать, а не делать это.Кроме того, этот пример предназначен только для поддержки моего вопроса, а не того, что я использую в реальном мире. – CKing

+1

Хотя эти два лямбда-выражения выглядят одинаково, они не то же самое. Их целевые типы совершенно разные, и поэтому это их намерение. Я полагаю, что предикат лямбда довольно плох, так как он имеет побочные эффекты. Чтобы сохранить ссылочную прозрачность, это должно быть 'l :: contains' –

ответ

4

Вы не определили, к какому «дублированию» вы беспокоитесь.

Если это дублирование кода (или скажем, страх по поводу возможных несоответствий между выражениями, которые намеренно должен быть таким же), что беспокоит вас, вы можете использовать

Predicate<List<String>> predicate = (l) -> l.add("1"); 
Consumer<List<String>> consumer = predicate::test; 

, чтобы избежать этого.

Если вы хотите, лямбда-выражений, которые будут действительно представлены в одном экземпляре, вы можете создали комбинированный interface:

interface PredicateAndConsumer<T> extends Predicate<T>, Consumer<T> { 
    public default void accept(T t) { test(t); } 
} 

PredicateAndConsumer<List<String>> pAndC = (l) -> l.add("1"); 
Predicate<List<String>> predicate = pAndC; 
Consumer<List<String>> consumer = pAndC; 

Итак, как вы можете видеть, вы можете использовать pAndC в обоих случаях, когда требуется либо Predicate<List<String>>, либо Consumer<List<String>>.

Однако при этом мало пользы. В этом частном случае мы можем с уверенностью предположить, что для большинства JRE накладные расходы на введение другого interface выше, чем сохранение его реализации через одно лямбда-выражение.


Вы также можете рассмотреть ответы на вопросы "Is method reference caching a good idea in Java 8?". Чтобы обобщить это, выражения вашего примера не фиксируют значения и, следовательно, будут реализованы как сингллеты в эталонной реализации Oracle (и, вероятно, будут и во всех альтернативных реализациях). Итак, здесь мы говорим о том, есть ли одна или две неизменяемые константы, созданные в среде выполнения ...

+0

Можете ли вы рассказать о том, что вы подразумеваете под« не записывать значения? » – CKing

+1

Упрощенные, они не имеют доступа к переменным из их окружения, см. Http://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html#accessing-local-variables – Holger

2

Шансы использовать один и тот же код для реализации двух разных интерфейсов в рамках одного и того же метода, давайте обратимся к нему практически с нуля. Но даже тогда вы можете делать то, что вы, вероятно, всегда делаете, когда сталкиваетесь с дублирующимся кодом: Создайте новый метод/класс.

public class LambdasTypeCheck { 
    public static void main(String []args) { 
    Predicate<List<String>> predicate = LambdasTypeCheck::addOne; 
    Consumer<List<String>> consumer = LambdasTypeCheck::addOne; 
    } 

    public static boolean addOne(List<String> list) { 
    return list.add("1"); 
    } 
} 

Это не работает, если вы хотите, чтобы захватить значения, конечно:

String one = "1"; 
Predicate<List<String>> predicate = (l) -> l.add(one); 
Consumer<List<String>> consumer = (l) -> l.add(one); 

Здесь вы должны использовать лямбда-выражения, и вы могли бы прибегнуть только к решению предложенной @Holger. Но опять же: Это (дублирование кода внутри метода) - очень и очень маловероятный сценарий :)

Смежные вопросы