Reputation: 2373
Functional Interface is described like this:
package models;
@FunctionalInterface
public interface EventReducer {
void apply(Event event, GameState state);
}
I implement the interface in the following class:
package models.reducers;
import models.Event;
import models.EventReducer;
import models.GameState;
import models.events.MinionDeathEvent;
public class MinionDeath implements EventReducer {
@Override
public void apply(Event event, GameState state) {
MinionDeathEvent deathEvent = (MinionDeathEvent)event;
deathEvent.getPlayer().getBoard().remove(deathEvent.getMinion());
}
}
How can I pass the implementation as a parameter? For example,
private static final Map<EventType, EventReducer> ReducersMap = Map.ofEntries(
entry(EventType.DEATH, MinionDeath::apply);
);
Apparently, MinionDeath::apply
is not a way to go
Upvotes: 2
Views: 1231
Reputation: 20185
In the current solution, we try to reference an instance method without having an instance of a class
entry(EventType.DEATH, MinionDeath::apply);
The runtime is not able to create an instance of MinionDeath
by itself, mainly because it is not guaranteed that a no-args constructor exists (it exists in this particular case, but not every class has a no-args constructor).
There are several possibilities to solve the problem. Here are some of them:
Create an instance of MinionDeath
, pass it as argument.
entry(EventType.DEATH, new MinionDeath());
Lambda expression: This is basically inlining the implementation of MinionDeath
diretcly into the lambda.
entry(
EventType.DEATH,
(event, state) -> ((MinionDeathEvent) event).getPlayer()
.getBoard()
.remove(deathEvent.getMinion()));
Define the lambda as, e.g., static method, pass it as method reference.
public class Game { // The class name is an assumption of mine
...
private static void processMonsterDeathEvent(Event event, GameState state) {
MinionDeathEvent deathEvent = (MinionDeathEvent)event;
deathEvent.getPlayer().getBoard().remove(deathEvent.getMinion());
}
private static final Map<EventType, EventReducer> ReducersMap = Map.ofEntries(
entry(EventType.DEATH, Game::processMonsterDeathEvent)
);
...
}
A remark on this solution: This is different from the original solution since we are now referencing a static method. We could also change method apply
in MinionDeath
to be static
. In this case, we should remove the ... implements EventRecorder
from the class definition since it is no longer needed.
Upvotes: 5
Reputation: 44398
You want to pass an instance of the MinionDeath
as long as it is a functional interface itself with exactly one abstract method, therefore is qualified. Also MinionDeath
is EventReducer
and is assignable into that class.
private static final Map<EventType, EventReducer> ReducersMap = Map.ofEntries(
entry(EventType.DEATH, new MinionDeath())
);
Note this is effectively as same as you pass the lambda expression itself:
private static final Map<EventType, EventReducer> ReducersMap = Map.ofEntries(
entry(
EventType.DEATH, // key
(event, state) -> { // value
MinionDeathEvent deathEvent = (MinionDeathEvent)event;
deathEvent.getPlayer().getBoard().remove(deathEvent.getMinion());
}
)
);
Also note, if you write entry(EventType.DEATH, MinionDeath::apply)
, it is not what you want and a new functional interface compatible with such lambda expression would have been created:
interface TriConsumer<T,R,S> {
void consume(T t, R r, S s);
}
Map<EventType, TriConsumer<MinionDeath, Event, GameState>> map = new HashMap<>();
map.put(EventType.DEATH, MinionDeath::apply);
Upvotes: 1