将功能接口的实现作为参数传递

Pass implementation of functional interface as parameter

功能接口描述如下:

package models;

@FunctionalInterface
public interface EventReducer {
    void apply(Event event, GameState state);
}

我实现接口如下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());
    }
}

如何将实现作为参数传递?例如,

    private static final Map<EventType, EventReducer> ReducersMap = Map.ofEntries(
        entry(EventType.DEATH, MinionDeath::apply);
    );

显然,MinionDeath::apply 不是一条路要走

问题

在当前的解决方案中,我们尝试在没有 class

实例的情况下引用实例方法
entry(EventType.DEATH, MinionDeath::apply);

运行时无法自行创建 MinionDeath 的实例,主要是因为不能保证 no-args 构造函数存在(在这种特殊情况下它存在,但不是每个 class 有一个 no-args 构造函数)。

解决方案

有几种解决问题的可能性。以下是其中一些:

  1. 创建 MinionDeath 的实例,将其作为参数传递。

    entry(EventType.DEATH, new MinionDeath());
    
  2. Lambda 表达式:这基本上是将 MinionDeath 的实现直接内联到 lambda 中。

    entry(
        EventType.DEATH, 
        (event, state) -> ((MinionDeathEvent) event).getPlayer()
                .getBoard()
                .remove(deathEvent.getMinion()));
    
  3. 将 lambda 定义为静态方法,将其作为方法引用传递。

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

    对该解决方案的评论:这与原始解决方案不同,因为我们现在引用的是静态方法。我们还可以将 MinionDeath 中的方法 apply 更改为 static。在这种情况下,我们应该从 class 定义中删除 ... implements EventRecorder,因为它不再需要了。

你想传递一个 MinionDeath 的实例,只要它 一个功能接口本身,只有一个抽象方法,因此是合格的。另外 MinionDeathEventReducer 并且可以分配给 class.

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