Đối với máy chủ trò chơi Java của tôi, tôi gửi ID hành động của gói mà về cơ bản cho máy chủ biết gói tin là gì. Tôi muốn ánh xạ từng Action ID (một số nguyên) vào một hàm. Có cách nào để làm điều này mà không cần sử dụng công tắc không?Con trỏ chức năng/đại biểu trong Java?
Trả lời
Còn cái này thì sao?
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();
(Nếu bạn cần chuyển một số đối số, hãy xác định giao diện của riêng bạn bằng hàm có thông số phù hợp và sử dụng thay vì Runnable).
Tôi nghĩ rằng một chuyển đổi trong đó functionA và functonB được gọi sẽ là mã rõ ràng hơn nhiều. Điều này chỉ di chuyển ánh xạ các ID để hoạt động vào phần mã nơi bạn đang tải bản đồ, thay vì nơi bạn đang gọi hàm. –
vâng, tôi đồng ý một công tắc sẽ sạch hơn. tôi cũng sẽ sử dụng một công tắc. nhưng tôi nghĩ rằng bạn nên nói với nó là người hỏi ban đầu (trong phần bình luận OP) :) –
Tôi đặt nó trong câu trả lời của tôi để thay thế. –
Java không có con trỏ hàm hạng nhất. Để đạt được chức năng tương tự, bạn phải xác định và thực hiện một giao diện. Bạn có thể làm cho nó dễ dàng hơn bằng cách sử dụng các lớp bên trong vô danh, nhưng nó vẫn không phải là rất đẹp. Dưới đây là ví dụ:
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
Để biết thêm về "con trỏ chức năng" trong Java, hãy xem: http://stackoverflow.com/q/122407/545127 – Raedwald
Bạn đã từng sử dụng Swing/AWT chưa? Phân cấp sự kiện của họ giải quyết một vấn đề tương tự. Cách Java qua chức năng xung quanh là với một giao diện, ví dụ
public interface ActionHandler {
public void actionPerformed(ActionArgs e);
}
Sau đó, nếu bạn muốn ánh xạ số nguyên vào các đối tượng này, bạn có thể sử dụng một cái gì đó giống như một java.util.HashMap<Integer,ActionHandler>
để quản lý đó. Việc triển khai thực tế có thể đi vào các lớp ẩn danh (xấp xỉ tốt nhất của Java về "lambda") hoặc trong các lớp thích hợp ở đâu đó. Đây là lớp cách nặc danh:
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);
(chỉnh sửa) Nếu bạn muốn trở thành thậm chí lạm dụng nhiều hơn, bạn có thể khởi tạo HashMap như vậy:
HashMap<Integer,String> m = new HashMap<Integer,String>() {{
put(0,"hello");
put(1,"world");
}};
Java không thực sự có chức năng gợi ý (chúng tôi có các lớp bên trong vô danh thay thế). Mặc dù vậy, thực sự không có gì sai khi sử dụng chuyển đổi, miễn là bạn đang chuyển sang giá trị chứ không phải trên loại. Có lý do gì mà bạn không muốn sử dụng công tắc không? Có vẻ như bạn sẽ phải thực hiện ánh xạ giữa ID hành động và hành động ở đâu đó trong mã của bạn, vậy tại sao không giữ nó đơn giản?
Vâng, suy nghĩ của tôi là như nhau. Bản thân tôi sẽ sử dụng công tắc. Tôi tin rằng các công tắc hậu trường được xử lý theo cách truy cập ngẫu nhiên hơn là kiểm tra tuần tự các trường hợp như bạn phải làm với if/else. Chỉ có điều là các chỉ số trường hợp nên được liên tiếp tôi nghĩ ...? –
Họ không phải liên tiếp. Bạn có thể đặt các trường hợp có khả năng nhất trước tiên nếu bạn cần tối ưu hóa. (Luôn luôn đo lường trước khi bạn tối ưu hóa.) –
Một tuyên bố chuyển đổi không cho phép các lớp trong các phần khác nhau của mã của mình tải lại động. Điều này ngăn chặn các tính năng nâng cao như tải callbacks từ các tập tin cấu hình trong thời gian chạy và như vậy.Tôi muốn khuyên bạn nên chống lại báo cáo chuyển đổi ủng hộ một giải pháp mạnh mẽ hơn vì lý do này. –
Bạn có thể thực hiện việc này thông qua việc sử dụng chuỗi mô hình trách nhiệm.
Đây là mẫu liên kết các đối tượng khác nhau với nhau giống như danh sách được liên kết. tức là mỗi đối tượng có tham chiếu đến tiếp theo trong chuỗi. Các đối tượng trong chuỗi thường xử lý một hành vi cụ thể. Luồng giữa các đối tượng rất giống với câu lệnh switch-case.
Có một số gotchas, chẳng hạn như, nó lây lan logic của bạn ra, một chuỗi quá dài có thể gây ra vấn đề hiệu suất. Nhưng cùng với những gotchas bạn có lợi ích của tăng testability, và sự gắn kết mạnh mẽ hơn. Ngoài ra, bạn không bị giới hạn sử dụng các biểu thức enum, byte, int ngắn và char làm trigger cho phân nhánh.
Ngọt. Tôi đã bình chọn không có lời giải thích. Tôi đoán người đó không thích cách tôi gõ câu trả lời của tôi. Có lẽ những gì tôi nói là sai. Tôi đoán là người đó có vấn đề với CoR. Nhưng tôi chỉ đoán vì không có bình luận nào về lý do tại sao. –
Mẫu này là gì? Làm cách nào để giải quyết/phá vỡ vấn đề sử dụng mã để chuyển đổi nhiều chức năng? Một ví dụ, hoặc ít nhất một liên kết, đã lưu câu trả lời này. Nhưng có, tôi không thích cách bạn nhập câu trả lời của bạn. –
Cảm ơn bạn. Đó là tất cả những gì tôi hỏi. Nếu bạn có can đảm để downvote ai đó có can đảm để đưa ra một lý do. –
Kiểm tra các bao đóng đã được triển khai như thế nào trong thư viện lambdaj. Họ thực sự có một hành vi rất giống với các đại biểu C#:
Yeah, nhưng sử dụng một giao diện có nghĩa là bạn phải tạo một giao diện cho mỗi cuộc gọi lại có nghĩa là tất cả các chức năng bạn muốn vượt qua nó thiết lập. Việc tạo một lớp đại biểu để xử lý điều này cho bạn (không phải là một con trỏ hàm đúng) nhưng hàm sẽ được chuyển và nếu bạn lạm dụng một kiểu chung để trở thành kiểu trả về, bạn không cần phải cắt bỏ nút cổ chai xuống gần như không có gì.
Đại biểu C# (MultiCastDelegate là chính xác) lấy thông tin từ phương thức sử dụng MethodInfo, điều tương tự bạn cần làm cho lớp Delegate bằng cách sử dụng java.lang.reflect.method. Tôi đăng mã của tôi cho lớp Delegate (T) trên một hình thức khác của trang web này đối phó với vấn đề này extact. Tôi làm cho nó bởi vì (có) được từ C + + Tôi cần một cách tốt hơn cho các chức năng đi qua (đặc biệt là Void) sau đó phải tạo ra một giao diện cho chức năng hoặc nhiều hơn. Bây giờ tôi có thể chọn chức năng điền thông tin tham số cho nó. Thì đấy! Đẹp và có thể sử dụng mà không có sự mất mát đáng chú ý nào về tốc độ từ JIT hoặc JVM. Và nếu tôi chỉ học lập trình java trong một tuần, bất kỳ lập trình java nào cũng có thể làm được.
Ngoài ra, nó hoạt động rất tốt khi tạo trình nghe cơ sở và giao diện cơ sở để truyền trong trình nghe. Không còn phải viết một người nghe khác vì tên của hàm đã thay đổi. Tạo một lớp đại biểu có lợi thế rất lớn như là rất dễ sử dụng và khá.
Bạn có thể giao diện các phương pháp tĩnh. Phương pháp này cũng cho phép bạn chỉ định các tham số. Tuyên bố giao diện của bạn ...
public interface RouteHandler {
void handleRequest(HttpExchange t) throws IOException;
}
Và bản đồ của bạn ...
private Map<String, RouteHandler> routes = new HashMap<>();
Sau đó thực hiện phương pháp tĩnh phù hợp với giao diện/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();
}
Bạn có thể sau đó thêm những phương pháp đó vào bản đồ của bạn ...
routes.put("/foo", CoreRoutes::notFound);
và gọi cho họ như sau ...
RouteHandler handler = routes.get("/foo");
handler.handleRequest(exchange);
Nhìn chung, chuyển đổi sẽ tốt hơn. – TheSoftwareJedi
Vâng, một công tắc gần như chắc chắn sẽ nhanh hơn, nhỏ hơn, sạch hơn. –
có thể trùng lặp của [Thay thế gần nhất cho một con trỏ hàm trong Java là gì?] (Http://stackoverflow.com/questions/122407/whats-the-nearest-substitute-for-a-function-pointer-in-java) – Raedwald