对于我的Java游戏服务器,我发送数据包的Action ID,它基本上告诉服务器数据包的用途。我想将每个Action ID(一个整数)映射到一个函数。有没有办法在不使用开关的情况下这样做?
答案 0 :(得分:21)
这个怎么样?
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)。
答案 1 :(得分:2)
Java实际上没有函数指针(我们得到了匿名内部类)。但是,使用交换机确实没有问题,只要你打开值而不是类型。你有什么理由不想使用开关吗?您似乎必须在代码中的某个位置执行操作ID和操作之间的映射,那么为什么不保持简单呢?
答案 2 :(得分:1)
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
答案 3 :(得分:1)
你曾经使用过Swing / AWT吗?他们的事件层次结构解决了类似的问题。 Java传递函数的方式是使用接口,例如
public interface ActionHandler {
public void actionPerformed(ActionArgs e);
}
然后,如果要将整数映射到这些对象上,可以使用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");
}};
答案 4 :(得分:1)
是的,但是使用接口意味着您必须为每个回调创建一个接口,这意味着您想要传递的每个功能都设置。创建一个委托类来处理这个问题会给你(不是一个真正的函数指针)但要传递的函数,如果你滥用泛型作为返回类型,你就不必强制转换将瓶颈降低到几乎为零。
c#delegate(MultiCastDelegate是正确的)从使用MethodInfo方法获取信息,这与使用java.lang.reflect.method对Delegate类需要做的事情相同。我在本网站的另一种形式上发布了代表(T)类的代码,处理这个问题。我之所以这样做是因为(是的)来自C ++,我需要一种更好的方式来传递函数(尤其是Void),然后必须为函数或更多函数创建一个接口。现在我可以选择填写参数信息的函数。 Voila`!很好,可用,没有明显的JIT或JVM速度损失。如果我只学习java编程一周,那么任何java程序员都可以做到。
此外,它在创建基本侦听器和基本接口以传入侦听器时非常有用。不再需要编写另一个侦听器,因为函数的名称已更改。创建委托类具有很大的优势,因为它非常有用且可以通过。
答案 5 :(得分:1)
您可以连接静态方法。此方法也允许您指定参数。声明你的界面...
public interface RouteHandler {
void handleRequest(HttpExchange t) throws IOException;
}
你的地图......
private Map<String, RouteHandler> routes = new HashMap<>();
然后实现与interface / params ...
匹配的静态方法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);
答案 6 :(得分:0)
您可以通过使用责任链模式来实现这一目标。
这是一种将不同对象链接在一起的模式,就像链接列表一样。即每个对象在链中都有对next的引用。链中的对象通常处理一个特定的行为。对象之间的流程与switch-case语句非常相似。
有一些问题,例如,它将你的逻辑传播出来,过长的链可能会导致性能问题。但是,除了这些陷阱之外,您还可以获得更高的可测试性和更强的凝聚力。此外,您不限于使用enum,byte,int short和char表达式作为分支的触发器。
答案 7 :(得分:0)
检查闭包是如何在lambdaj库中实现它们的。它们实际上的行为与C#委托非常相似:
答案 8 :(得分:0)
另一个类似的方法可能是使用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();
答案 9 :(得分:0)
您可以使用java.util.function(对于Java 8+)中的东西来存储函数。 java.util.function docs
您必须浏览此列表并选择要使用的适当函数类。我将使用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
}