2015-10-07 3 views
5

У меня есть хорошее понимание лямбда-функций в Lisp. Кажется, что Java не обладает такой же гибкостью, как Lisp. Как мне думать о лямбдах в Java? Учитывая приведенный ниже код, как я могу это сделать?Как передать и использовать произвольные лямбда-функции в качестве параметра

public class Draw { 
    GraphicsContext gc; 

    static void draw(double x, double y, double w, double h, boolean drawRect) { 
     if (drawRect) { 
      gc.fillRect(x, y, w, h); 
     } else { 
      gc.strokeRect(x, y, w, h); 
     } 
    } 

    // How do I do that? 
    static void drawWithLambda(double x, double y, double w, double h /**, lambda */) { 
     lambda(x, y, w, h); 
    } 

    public static void main(String[] args) { 
     draw(0, 0, 50, 50, false); // OK 
     drawWithLambda(0, 0, 50, 50, GraphicsContext::fillRect); // Ok? 
    } 
} 
+3

Прочтите эту статью: http://stackoverflow.com/questions/13604703/how-do-i-define-a-method-which-takes-a-lambda-as-a-parameter-in-java-8 – PiotrSliwa

ответ

3

Вы должны указать тип функционального интерфейса, который lambda метод аргумент реализует:

drawWithLambda(double x, double y, double w, double h, SomeFuncInterface lambda) { 
    lambda.someMethod (x, y, w, h); // someMethod is the single method that 
            // SomeFuncInterface implements 
} 

Для того, чтобы эта линию работать

drawWithLambda(0, 0, 50, 50, GraphicsContext::fillRect); 

метод fillRect из GraphicsContext сусла быть совместимым с сигнатурой метода функционального интерфейса SomeFuncInterface.

BTW, GraphicsContext::fillRect - ссылка на метод, а не выражение лямбда. Вы можете передавать либо лямбда-выражения, ссылки на методы, либо любую другую реализацию функционального интерфейса (обычный экземпляр класса, экземпляр анонимного класса и т. Д.).

+1

'GraphicsContext :: fillRect' не будет компилироваться, потому что вы не можете статически ссылаться на не-st atic метод 'fillRect'. Вам нужно использовать 'gc :: fillRect', где' gc' является экземпляром 'GraphicsContext'. – Tunaki

+1

@ Tunaki Ответ был вообще. Я не знаком с классом GraphicsContext, поэтому я не знал, был ли «fillRect» статичным или нет. – Eran

3

Лямбда в Java работает в сочетании с понятием functional interface.

Типичный пример: Function. Function - это функциональный интерфейс, функциональный метод которого apply - это метод, который принимает один аргумент и возвращает результат.

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

@FunctionalInterface 
interface RectangleDrawer { 
    void draw(double x, double y, double w, double h); 
} 

(FunctionalInterface аннотаций не является строго необходимыми, но это дает четкое намерение).

Затем вы можете создать лямбда, которая соответствует контракту этого функционального интерфейса. Типичный lambda syntax: (method arguments) -> (lambda body). В этом примере это будет: (x, y, w, h) -> gc.fillRect(x, y, w, h). Это компилируется, потому что лямбда объявляет 4 аргумента и не имеет типа возврата, поэтому его можно представить как функциональный метод RectangleDrawer, определенный ранее.

Вы пример стал бы:

static GraphicsContext gc; 

public static void main(String[] args) { 
    draw(0, 0, 50, 50, (x, y, w, h) -> gc.fillRect(x, y, w, h)); 
    draw(0, 0, 50, 50, (x, y, w, h) -> gc.strokeRect(x, y, w, h)); 
} 

static void draw(double x, double y, double w, double h, RectangleDrawer drawer) { 
    drawer.draw(x, y, w, h); 
} 

В данном конкретном случае, можно использовать method reference создать лямбда, используя :: оператора, что позволяет писать более простой код:

static GraphicsContext gc; 

public static void main(String[] args) { 
    draw(0, 0, 50, 50, gc::fillRect); 
    draw(0, 0, 50, 50, gc::strokeRect); 
} 

static void draw(double x, double y, double w, double h, RectangleDrawer drawer) { 
    drawer.draw(x, y, w, h); 
} 
+0

спасибо. Я думаю, что у меня есть. Для каждой функции, которую я хочу передать как параметр, я должен определить интерфейс am, содержащий только одно определение функции с той же сигнатурой, что и функция, которую я хотел бы использовать. – user2407434

+1

@ user2407434 Вам не обязательно всегда определять interace, вы можете использовать уже существующие в ['java.util.function'] (https://docs.oracle.com/javase/8/docs/ api/java/util/function/package-summary.html). – Tunaki

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