英文:
Pass implementation of functional interface as parameter
问题
Functional Interface被描述如下:
package models;
@FunctionalInterface
public interface EventReducer {
void apply(Event event, GameState state);
}
我在以下类中实现了该接口:
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());
}
}
我如何将实现作为参数传递?例如,
private static final Map<EventType, EventReducer> ReducersMap = Map.ofEntries(
entry(EventType.DEATH, MinionDeath::apply);
);
显然,MinionDeath::apply
不是正确的方法。
英文:
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
答案1
得分: 4
问题
在当前的解决方案中,我们尝试引用一个实例方法,而没有类的实例
entry(EventType.DEATH, MinionDeath::apply);
运行时不能够自行创建MinionDeath
的实例,主要是因为不能保证存在无参构造函数(在这个特定情况下是存在的,但并非每个类都有无参构造函数)。
解决方案
有几种可能的解决方案。以下是其中一些:
- 创建
MinionDeath
的实例,将其作为参数传递。
entry(EventType.DEATH, new MinionDeath());
- Lambda表达式:这实际上是将
MinionDeath
的实现直接内联到lambda中。
entry(
EventType.DEATH,
(event, state) -> ((MinionDeathEvent) event).getPlayer()
.getBoard()
.remove(deathEvent.getMinion()));
- 将lambda定义为静态方法,将其作为方法引用传递。
public class Game { // 类名是我假设的
...
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)
);
...
}
关于这个解决方案的备注:这与原始解决方案不同,因为我们现在引用的是一个静态方法。我们还可以将MinionDeath
中的方法apply
更改为static
。在这种情况下,我们应该从类定义中删除... implements EventRecorder
,因为它不再需要。
英文:
The problem
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).
Solutions
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
inMinionDeath
to bestatic
. In this case, we should remove the... implements EventRecorder
from the class definition since it is no longer needed.
答案2
得分: 1
private static final Map<EventType, EventReducer> ReducersMap = Map.ofEntries(
entry(EventType.DEATH, new MinionDeath())
);
请注意,这实际上与直接传递lambda表达式是相同的:
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());
}
)
);
另请注意,如果您编写entry(EventType.DEATH, MinionDeath::apply)
,这不是您想要的,会创建一个与此类lambda表达式兼容的新函数式接口:
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);
英文:
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);
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论