19

对于我的 Java 游戏服务器,我发送数据包的操作 ID,它基本上告诉服务器数据包的用途。我想将每个动作 ID(一个整数)映射到一个函数。有没有办法在不使用开关的情况下做到这一点?

4

10 回答 10

26

这个如何?

HashMap<Integer, Runnable> map = new HashMap<Integer, Runnable>();
map.put(Register.ID, new Runnable() { 
    public void run() { functionA(); }
});
map.put(NotifyMessage.ID, new Runnable() { 
    public void run() { functionB(); }
});
// ...
map.get(id).run();

(如果您需要传递一些参数,请使用具有合适参数的函数定义您自己的接口,并使用它而不是 Runnable)。

于 2008-12-28T04:30:39.550 回答
3

另一种类似的方法可能是使用 Java 8 的供应商:

Map<Integer, Supplier<T>> suppliers = new HashMap();

suppliers.put(1, () -> methodOne());
suppliers.put(2, () -> methodTwo());

// ...

public T methodOne() { ... }
public T methodTwo() { ... }

// ...

T obj = suppliers.get(id).run();
于 2019-01-10T19:08:53.560 回答
2

Java 没有一流的函数指针。为了实现类似的功能,您必须定义和实现一个接口。您可以使用匿名内部类使其更容易,但它仍然不是很漂亮。这是一个例子:

public interface PacketProcessor
{
    public void processPacket(Packet packet);
}

...

PacketProcessor doThing1 = new PacketProcessor()
{
    public void processPacket(Packet packet)
    {
        // do thing 1
    }
};
// etc.

// Now doThing1, doThing2 can be used like function pointers for a function taking a
// Packet and returning void
于 2008-12-28T04:30:19.770 回答
2

Java 并没有真正的函数指针(我们有匿名内部类)。不过,使用开关确实没有什么问题,只要您打开的是值而不是类型。你有什么不想使用开关的原因吗?似乎您必须在代码中某处的操作 ID 和操作之间进行映射,所以为什么不保持简单呢?

于 2008-12-28T04:34:43.553 回答
1

你用过 Swing/AWT 吗?他们的事件层次结构解决了类似的问题。Java 传递函数的方式是使用接口,例如

public interface ActionHandler {
    public void actionPerformed(ActionArgs e);
}

然后,如果你想将整数映射到这些对象上,你可以使用类似 a 的东西java.util.HashMap<Integer,ActionHandler>来管理它。实际的实现既可以放在匿名类(Java 对“lambda”的最佳近似)中,也可以放在某个适当的类中。这是匿名类方式:

HashMap<Integer,ActionHandler> handlers;
handlers.put(ACTION_FROB, new ActionHandler() {
    public void actionPerformed(ActionArgs e) {
        // Do stuff
        // Note that any outer variables you intend to close over must be final.
    }
});
handlers.get(ACTION_FROB).actionPerformed(foo);

(编辑)如果你想更加滥用,你可以像这样初始化 HashMap:

HashMap<Integer,String> m = new HashMap<Integer,String>() {{
    put(0,"hello");
    put(1,"world");
}};
于 2008-12-28T04:32:50.420 回答
1

是的,但是使用接口意味着您必须为每个回调创建一个接口,这意味着您要设置的每个函数。创建一个委托类来处理这个给你(不是一个真正的函数指针)但是要传递的函数,如果你滥用泛型作为返回类型,你不必强制转换,这将瓶颈减少到几乎没有。

c# 委托(MultiCastDelegate 是正确的)从使用 MethodInfo 方法获取信息,这与使用 java.lang.reflect.method 为 Delegate 类执行的操作相同。我在本网站的另一种形式上发布了我的 Delegate(T) 类的代码,以处理这个确切的问题。我这样做是因为(是的)来自 C++ 我需要一种更好的方法来传递函数(尤其是 Void),然后必须为 on 函数或更多函数创建接口。现在我可以选择为其填写参数信息的函数。瞧`! JIT 或 JVM 的速度没有明显的损失,而且很好用。如果我只学习了一周的java编程,任何java程序员都可以做到。

此外,它在创建基础侦听器和传入侦听器的基础接口时非常有用。不再需要编写另一个侦听器,因为函数的名称已更改。创建一个委托类具有很大的优势,因为它非常有用且可以通过。

于 2011-03-02T07:05:53.197 回答
1

您可以接口静态方法。此方法也允许您指定参数。声明你的界面...

public interface RouteHandler {
    void handleRequest(HttpExchange t) throws IOException;
}

还有你的地图...

private Map<String, RouteHandler> routes = new HashMap<>();

然后实现与接口/参数匹配的静态方法...

public static void notFound(HttpExchange t) throws IOException {
    String response = "Not Found";

    t.sendResponseHeaders(404, response.length());
    OutputStream os = t.getResponseBody();
    os.write(response.getBytes());
    os.close();
}

然后,您可以将这些方法添加到您的地图中......

routes.put("/foo", CoreRoutes::notFound);

并按如下方式称呼他们...

RouteHandler handler = routes.get("/foo");
handler.handleRequest(exchange);
于 2017-10-13T08:40:16.933 回答
0

您可以通过使用责任链模式来做到这一点。

它是一种将不同对象链接在一起的模式,有点像链表。即每个对象都有对链中下一个的引用。链中的对象通常处理一种特定的行为。对象之间的流程与 switch-case 语句非常相似。

有一些陷阱,例如,它会分散你的逻辑,过长的链会导致性能问题。但是除了这些陷阱,您还可以提高可测试性和更强的凝聚力。此外,您不限于使用 enum、byte、int short 和 char 表达式作为分支的触发器。

于 2008-12-28T05:14:41.663 回答
0

检查闭包是如何在 lambdaj 库中实现的。它们实际上具有与 C# 委托非常相似的行为:

http://code.google.com/p/lambdaj/wiki/Closures

于 2009-09-12T13:46:09.090 回答
0

您可以使用 java.util.function(对于 Java 8+)中的内容来存储函数。 java.util.function 文档

你必须浏览这个列表并选择一个合适的函数类来使用。我将使用 BiConsumer,它说:

Takes two inputs. Returns no result. Operates through side effects.

import java.util.function.BiConsumer;
import java.util.HashMap;
import java.util.Map;

public void init(){
  //<actionId, function<arg1, arg2>>
  Map<Integer, BiConsumer<String, String>> actionMap = new HashMap<>();
  actionMap.put(123, this::someMethod);

  //note (optional): check key first: map.containsKey(someActionId)
  //call someMethod("argument1", "argument2")
  actionMap.get(123).accept("argument1", "argument2");
}

public void someMethod(String a, String b){
  //do something here
}
于 2020-06-22T14:01:25.257 回答