【问题标题】:Pass implementation of functional interface as parameter将功能接口的实现作为参数传递
【发布时间】:2020-08-30 09:22:24
【问题描述】:

功能接口描述如下:

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 不是办法

【问题讨论】:

  • 您需要实例化MinionDeath 的新实例并将其作为参数传递:entry(EventType.DEATH, new MinionDeath());。 --- 顺便说一句:entry(...) 后缺少分号
  • 我必须初始化它吗? ::没有办法?
  • 如果您将其作为单独的类实现,则不会。您可以做的是将其转换为表达式 lambda:entry(EventType.DEATH, (event, state) -&gt; ((MinionDeathEvent) event).getPlayer().getBoard().remove(deathEvent.getMinion()));
  • 如果您喜欢,请创建一个答案
  • 您的EventReducer 似乎与BiConsumer 非常相似。您是否不想通过 interface EventReducer&lt;T&gt; { void apply(T event, GameState state);} 后跟 new EventReducer&lt;MinionDeathEvent&gt; { @Override public void apply(MinionDeathEvent event, GameState state) { event.getPlayer().getBoard().remove(deathEvent.getMinion()); } 之类的东西来引入对类型的认识?

标签: java java-8 functional-interface


【解决方案1】:

问题

在当前的解决方案中,我们尝试在没有类的实例的情况下引用实例方法

entry(EventType.DEATH, MinionDeath::apply);

运行时无法自己创建MinionDeath的实例,主要是因为不能保证存在无参数构造函数(在这种特殊情况下存在,但并非每个类都有无参数构造函数)。

解决方案

解决问题的方法有多种。以下是其中一些:

  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。在这种情况下,我们应该从类定义中删除... implements EventRecorder,因为它不再需要。

【讨论】:

    【解决方案2】:

    您想传递MinionDeath 的实例,只要它一个功能接口本身,只有一个抽象方法,因此是合格的。 MinionDeath 也是 EventReducer 并且可以分配到该类中。

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

    【讨论】:

    • 是和不是。我想提供一个更直接的答案,并解释MinionDeath::apply 是什么。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-29
    • 1970-01-01
    • 2011-12-04
    • 1970-01-01
    • 2020-12-20
    相关资源
    最近更新 更多