Подтвердить что ты не робот

Java 8 - магазин lambdas в списке

Интересно, можно ли хранить лямбда в каком-то контейнере, например. ArrayList или HashMap. Я хочу изменить этот код:

public enum OPCODE implements BinaryOperator<Integer> {
    MOV((x, y) -> y),
    INC((x, y) -> ++x),
    DEC((x, y) -> --x),
    ADD((x, y) -> x + y),
    SUB((x, y) -> x - y);

    private final BinaryOperator<Integer> binaryOperator;

    OPCODE(BinaryOperator<Integer> binaryOperator) {
        this.binaryOperator = binaryOperator;
    }  

    @Override
    public Integer apply(Integer integer, Integer integer2) {
        return binaryOperator.apply(integer, integer2);
    }
}

Что-то вроде:

List<BinaryOperator<Integer>> opcodes = new ArrayList<BinaryOperator<Integer>>(){
    ((x, y) -> y),
    ((x, y) -> ++x)
};

и др.

и используйте его так:

opcodes[0].apply(a, b);

Возможно ли это?

4b9b3361

Ответ 1

Вы можете создать такой список, как:

List<BinaryOperator<Integer>> opcodes = Arrays.asList((x, y) -> y, (x, y) -> ++x);

// sample
int a=14,b=16;
System.out.println(opcodes.get(0).apply(a, b)); // prints 16
System.out.println(opcodes.get(1).apply(a, b)); // prints 15

Или исправление того, как вы пытались инициализировать список

List<BinaryOperator<Integer>> opcodes = new ArrayList<BinaryOperator<Integer>>() {{
    add((x, y) -> y);
    add((x, y) -> ++x);
    add((x, y) -> --x);
    add((x, y) -> x + y);
    add((x, y) -> x - y);
}};

Ответ 2

В дополнительном ответе @nullpointer вы также можете использовать клавишу Map, чтобы сохранить исходное OPCODE намерение функций, которое было бы lst в массиве, например. используя Enum в качестве ключа:

public enum OPCODES {
    MOV, ADD, XOR
}

Что может быть загружено:

Map<OPCODES, BinaryOperator<Integer>> opcodeMap = 
  new EnumMap<OPCODES, BinaryOperator<Integer>>(OPCODES.class);
opcodeMap.put(OPCODES.ADD, (x, y)-> x + y);
opcodeMap.put(OPCODES.MOV, (x, y) -> y);
opcodeMap.put(OPCODES.XOR, (x, y) -> x ^ y);

И используется:

System.out.println(opcodeMap.get(OPCODES.ADD).apply(1, 2));
System.out.println(opcodeMap.get(OPCODES.MOV).apply(1, 2));
System.out.println(opcodeMap.get(OPCODES.XOR).apply(1, 2));

Ответ 3

Вы можете хранить лямбда внутри контейнера, но реальный вопрос - почему вы хотите это сделать? Хранение их в List легко, например, Set/Map - вы не можете переопределить equals/hashcode для lambdas - так что вы не можете сказать, что произойдет.

Поскольку у вас уже есть Enum, почему бы не использовать более простой метод:

Set<OPCODE> set = EnumSet.allOf(OPCODE.class);

Ответ 4

Следовательно, вы определили своего оператора, как только сможете сделать что-то вроде этого:

List<BinaryOperator<Integer>> opcodes = new ArrayList<BinaryOperator<Integer>>() {{
    add(OPCODE.ADD);
    add(OPCODE.DEC);
}};

чтобы проверить это в вашем основном методе:

opcodes.forEach(elm -> System.out.println(elm.apply(1,2)));

Ответ 5

Да, вы можете легко положить lambdas в список или значения карты. Помните, что лямбды - это просто причудливый способ писать анонимные классы, которые, в свою очередь, являются лишь частным случаем оператора new. Другими словами, operators.add((x, y) -> x + y) является просто сокращением для

final BinaryOperator<Integer> ADD = new BinaryOperator<Integer>() {
    @Override
    public Integer apply(final Integer x, final Integer y) {
        return x + y;
    }
};
operators.add(ADD);

По той же логике operatorMap.put("add", (x, y) -> x + y); также будет делать то, что вы ожидаете.

Однако, включение lambdas в набор, включающий использование их в качестве клавиш карты, может не делать того, что вы ожидаете. Как правило, поведение набора зависит от определения equals и hashCode по его типу элемента, и язык не дает никаких гарантий для тех методов, которые не соответствуют тем, что определено в определении Object. Следовательно, следующее утверждение может завершиться неудачно:

final Function<Object, String> func1 = Object::toString;
final Function<Object, String> func2 = Object::toString;
assert func1.equals(func2);

Аналогично:

final Function<Object, String> func = Object::toString;
final Set<Object> set = new HashSet<>();
set.add(func);
assert set.contains(Object::toString);

Итак, будьте осторожны, ставя lambdas в контейнеры на основе Set, включая использование в качестве Map ключей, но их можно поместить в List и использовать как значения Map просто отлично.