Jump3r
Jump3r

Reputation: 1028

Java 8 - store lambdas in List

I wonder if it's possible to store lambdas in some container, for ex. ArrayList or HashMap. I want to change that code:

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);
    }
}

To something like:

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

etc.

and use it like so:

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

It is even possible?

Upvotes: 16

Views: 8203

Answers (6)

Mahmoud
Mahmoud

Reputation: 11431

Refining on @naomimyselfandi's answer.

You can use regular Function with some currying magic:

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

Please refer below for more details.

@Holger comment on: Can a java lambda have more than 1 parameter?

Upvotes: 0

Eugene
Eugene

Reputation: 120848

You could store lambdas inside a container, but the real question is why would you want to do that? Storing them in a List is easy, what about a Set/Map for example - you can't override equals/hashcode for lambdas - so you can't tell what would happen.

Since you already have an Enum there, why not use the simpler method:

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

Upvotes: 3

naomimyselfandi
naomimyselfandi

Reputation: 355

Yes, you can put lambdas in a list or values of a map just fine. Remember that lambdas are just a fancy way of writing anonymous classes, which in turn are just a special case of the new operator. Put another way, operators.add((x, y) -> x + y) is just shorthand for

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

By the same logic, operatorMap.put("add", (x, y) -> x + y); would also do exactly what you expect.

However, putting lambdas into a set - which includes using them as map keys - might not do what you expect. Generally, the behavior of a set depends on the definition of equals and hashCode by its element type, and the language doesn't make any guarantees for those methods beyond what's mandated by the definition of Object. Therefore, the following assert can fail:

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

Likewise the following:

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

So, be careful putting lambdas into Set-based containers, including use as Map keys, but they can be put into Lists and used as Map values just fine.

Upvotes: 0

StuartLC
StuartLC

Reputation: 107247

In additional @nullpointer's great answer, you can also consider using a Map key to retain the original OPCODE intention of the functions, which would be lst in the array, e.g. using an Enum as a key:

public enum OPCODES {
    MOV, ADD, XOR
}

Which can be bootstrapped:

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);

And used:

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));

Upvotes: 9

Maytham Fahmi
Maytham Fahmi

Reputation: 33397

Hence you have defined your operator once you can do some thing like this:

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

to test that in your main method:

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

Upvotes: 2

Naman
Naman

Reputation: 31878

You can certainly create such a list as:

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

Or redressing the way you were trying to initializing the list

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);
}};

Upvotes: 12

Related Questions