Как сопоставить лямбда-выражения в Java

Я пришел из Python и пытаюсь понять, как лямбда-выражения по-разному работают в Java. В Python вы можете делать такие вещи, как:

opdict = { "+":lambda a,b: a+b, "-": lambda a,b: a-b, 
           "*": lambda a,b: a*b, "/": lambda a,b: a/b }
sum = opdict["+"](5,4) 

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

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

Map<String, MathOperation> opMap = new HashMap<String, MathOperation>(){
        { put("+",(a,b)->b+a);
          put("-",(a,b)->b-a);
          put("*",(a,b)->b*a);
          put("/",(a,b)->b/a); }
};
...
...

interface MathOperation {
   double operation(double a, double b);
}

Однако это дает ошибку:

Целевой тип этого выражения должен быть функциональным интерфейсом.

Где именно я объявляю интерфейс?


person b_pcakes    schedule 11.10.2015    source источник


Ответы (2)


Достаточно просто сделать это с помощью BiFunction в Java 8:

final Map<String, BiFunction<Integer, Integer, Integer>> opdict = new HashMap<>();
opdict.put("+", (x, y) -> x + y);
opdict.put("-", (x, y) -> x - y);
opdict.put("*", (x, y) -> x * y);
opdict.put("/", (x, y) -> x / y);

int sum = opdict.get("+").apply(5, 4);
System.out.println(sum);

Синтаксис немного более подробный, чем Python, чтобы быть уверенным, и использование getOrDefault на opdict, вероятно, было бы предпочтительнее, чтобы избежать сценария, в котором вы используете оператор, которого не существует, но это должно по крайней мере запустить мяч.

Если вы работаете исключительно с ints, использование IntBinaryOperator было бы предпочтительнее, так как это позаботится о любой общей типизации, которую вам нужно будет выполнить.

final Map<String, IntBinaryOperator> opdict = new HashMap<>();
opdict.put("+", (x, y) -> x + y);
opdict.put("-", (x, y) -> x - y);
opdict.put("*", (x, y) -> x * y);
opdict.put("/", (x, y) -> x / y);

int sum = opdict.get("+").applyAsInt(5, 4);
System.out.println(sum);
person Makoto    schedule 11.10.2015
comment
IntBinaryOperator может быть немного лучше. - person Paul Boddington; 12.10.2015
comment
@PaulBoddington: Да, вы определенно правы, но тогда мы сталкиваемся с неловким сценарием: что, если мы хотим иметь возможность добавлять вещи, которые не просто целые вместе? Я включу то, что вы мне показали, спасибо за это. - person Makoto; 12.10.2015
comment
Да, такие сценарии могут раздражать. Когда мы в конечном итоге получим Map.of("+", (x, y) -> x + y, "-", ..., он будет почти таким же коротким, как версия Python. - person Paul Boddington; 12.10.2015
comment
Отличный ответ, всего один дополнительный вопрос; Как бы я это сделал, если бы захотел реализовать свой собственный интерфейс? Насколько я понимаю, когда вы создаете интерфейс с помощью лямбда-выражения, вы в основном реализуете абстрактный метод интерфейса. Пожалуйста, посмотрите редактирование, которое я внес в сообщение - person b_pcakes; 12.10.2015
comment
@SimonZhu: я не замечаю никаких сбоев компиляции, когда использую ваш пользовательский интерфейс. Я использую последнюю (я думаю) Java в версии 1.8.60; убедитесь, что вы в курсе, а также. - person Makoto; 12.10.2015

Альтернативным решением является использование перечисления:

public enum Operation {
    PLUS((x, y) -> x + y),
    MINUS((x, y) -> x - y),
    TIMES((x, y) -> x * y),
    DIVIDE((x, y) -> x / y);

    private final IntBinaryOperator op;

    Operation(IntBinaryOperator op) { this.op = op; }

    public int apply(int x, int y) { return op.applyAsInt(x, y); }
}

Затем вы можете сделать:

int sum = Operation.PLUS.apply(5, 4);

Это не так лаконично, как другие решения, но использование перечисления вместо String означает, что при вводе Operation. в IDE вам будет представлен список всех возможных операций.

person Paul Boddington    schedule 11.10.2015