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

huangapple go评论73阅读模式
英文:

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&lt;EventType, EventReducer&gt; 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的实例,主要是因为不能保证存在无参构造函数(在这个特定情况下是存在的,但并非每个类都有无参构造函数)。

解决方案

有几种可能的解决方案。以下是其中一些:

  1. 创建MinionDeath的实例,将其作为参数传递。
entry(EventType.DEATH, new MinionDeath());
  1. Lambda表达式:这实际上是将MinionDeath的实现直接内联到lambda中。
entry(
    EventType.DEATH, 
    (event, state) -> ((MinionDeathEvent) event).getPlayer()
            .getBoard()
            .remove(deathEvent.getMinion()));
  1. 将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:

  1. Create an instance of MinionDeath, pass it as argument.

    entry(EventType.DEATH, new MinionDeath());
    
  2. Lambda expression: This is basically inlining the implementation of MinionDeath diretcly into the lambda.

    entry(
        EventType.DEATH, 
        (event, state) -&gt; ((MinionDeathEvent) event).getPlayer()
                .getBoard()
                .remove(deathEvent.getMinion()));
    
  3. 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&lt;EventType, EventReducer&gt; 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.

答案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&lt;EventType, EventReducer&gt; 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&lt;EventType, EventReducer&gt; ReducersMap = Map.ofEntries(
	entry(
        EventType.DEATH,                                                      // key
        (event, state) -&gt; {                                                   // 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&lt;T,R,S&gt; {
	void consume(T t, R r, S s);
}
Map&lt;EventType, TriConsumer&lt;MinionDeath, Event, GameState&gt;&gt; map = new HashMap&lt;&gt;();
map.put(EventType.DEATH, MinionDeath::apply);

huangapple
  • 本文由 发表于 2020年8月30日 17:22:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/63655926.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定