2008-12-28 7 views
10

Für meinen Java Game Server sende ich die Action ID des Pakets, die dem Server im Grunde sagt, wofür das Paket ist. Ich möchte jede Aktions-ID (eine Ganzzahl) einer Funktion zuordnen. Gibt es eine Möglichkeit, dies ohne einen Schalter zu tun?Funktionszeiger/Delegaten in Java?

+0

Insgesamt wird ein Schalter besser sein. – TheSoftwareJedi

+0

Ja, ein Schalter wird mit ziemlicher Sicherheit viel schneller, kleiner, sauberer sein. –

+1

mögliches Duplikat von [Was ist der nächste Ersatz für einen Funktionszeiger in Java?] (Http://stackoverflow.com/questions/122407/whats-the-nearest-substitute-for-a-function-pointer-in-java) – Raedwald

Antwort

21

Was ist mit diesem?

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

(Wenn Sie einige Argumente zu übergeben, definieren Sie Ihre eigene Schnittstelle mit einer Funktion einen geeigneten Parameter aufweist, und verwenden, die anstelle von Runnable).

+3

Ich denke, dass ein Schalter, wo functionA und functonB angerufen werden, viel klarer Code wäre. Dies verschiebt die Zuordnung von IDs zu Funktionen nur in den Codeabschnitt, in den Sie die Map laden, anstatt an die Stelle, an der Sie die Funktion aufrufen. –

+0

Ja, ich stimme zu, ein Schalter wäre sauberer. Ich würde auch einen Schalter benutzen. aber ich denke, du solltest es dem ursprünglichen Fragesteller erzählen (in den OP-Kommentaren) :) –

+0

Ich lege es stattdessen in meine Antwort. –

1

Java verfügt nicht über erstklassige Funktionszeiger. Um eine ähnliche Funktionalität zu erreichen, müssen Sie eine Schnittstelle definieren und implementieren. Sie können es einfacher machen, anonyme innere Klassen zu verwenden, aber es ist immer noch nicht sehr hübsch. Hier ein Beispiel:

+0

Für mehr "Funktionszeiger" in Java, sehen Sie: http://Stackoverflow.com/q/122407/545127 – Raedwald

1

Haben Sie jemals Swing/AWT verwendet? Ihre Ereignishierarchie löst ein ähnliches Problem. Die Art und Weise Java geht Funktionen ist um mit einer Schnittstelle, zum Beispiel

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

Dann, wenn Sie ganze Zahlen auf diese Objekte abbilden möchten, können Sie so etwas wie ein java.util.HashMap<Integer,ActionHandler> verwenden könnte, dass zu verwalten. Die eigentlichen Implementierungen können entweder in anonyme Klassen (Javas beste Annäherung an "Lambda") oder irgendwo in geeignete Klassen gehen. Hier ist die anonyme Klasse Art und Weise:

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

(edit) Wenn Sie noch missbräuchlich sein wollen, können Sie die HashMap initialisieren wie so:

HashMap<Integer,String> m = new HashMap<Integer,String>() {{ 
    put(0,"hello"); 
    put(1,"world"); 
}}; 
2

Java nicht wirklich Funktionszeiger haben (wir bekam stattdessen anonyme innere Klassen). Es ist jedoch nichts falsch daran, einen Schalter zu verwenden, solange Sie den Wert und nicht den Typ einschalten. Gibt es einen Grund, warum Sie keinen Schalter verwenden möchten? Es scheint so, als müssten Sie irgendwo im Code eine Zuordnung zwischen Aktions-IDs und Aktionen vornehmen. Warum also nicht einfach bleiben?

+0

Ja, mein Gedanke war derselbe. Ich selbst würde den Schalter benutzen. Ich glaube, dass Switches hinter den Kulissen auf zufällige Weise gehandhabt werden, anstatt nacheinander die Fälle zu prüfen, die Sie mit if/else machen müssten. Nur, dass die Fallindizes konsekutiv sein sollten, denke ich ...? –

+0

Sie müssen nicht fortlaufend sein. Sie können die wahrscheinlichsten Fälle zuerst eingeben, wenn Sie optimieren müssen. (Messen Sie immer, bevor Sie optimieren.) –

+0

Eine switch-Anweisung lässt Klassen in verschiedenen Teilen seines Codes Callbacks nicht dynamisch laden. Dies verhindert erweiterte Funktionen wie das Laden von Callbacks aus Konfigurationsdateien zur Laufzeit und so weiter.Ich würde aus diesem Grund gegen Switch-Aussagen zugunsten einer leistungsfähigeren Lösung empfehlen. –

0

Sie können dies durch die Verwendung der Kette der Verantwortlichkeitsmuster tun.

Es ist ein Muster, das verschiedene Objekte miteinander wie eine verknüpfte Liste verknüpft. h. jedes Objekt hat eine Referenz auf den nächsten in der Kette. Die Objekte in der Kette behandeln normalerweise ein bestimmtes Verhalten. Der Fluss zwischen den Objekten ist der switch-case-Anweisung sehr ähnlich.

Es gibt einige Fehler, wie z. B. es verbreitet Ihre Logik, eine übermäßig lange Kette kann zu Leistungsproblemen führen. Aber zusammen mit diesen Problemen haben Sie den Vorteil einer erhöhten Testbarkeit und einer stärkeren Kohäsion. Außerdem sind Sie nicht auf die Verwendung von enum-, byte-, int short- und char-Ausdrücken als Verzweigungsauslöser beschränkt.

+0

Süß. Ich wurde ohne Erklärung abgelehnt. Ich schätze, die Person mochte die Art nicht, wie ich meine Antwort eintippte. Vielleicht war das, was ich gesagt habe, falsch. Meine Vermutung ist, dass die Person ein Problem mit AdR hat. Aber ich rate nur, weil es keinen Kommentar gab, warum. –

+0

Was ist das für ein Muster? Wie löst/umgeht es das Problem, einen Code zum Umschalten mehrerer Funktionen zu verwenden? Ein Beispiel oder zumindest ein Link hätte diese Antwort gespeichert. Aber ja, ich mochte nicht die Art, wie Sie Ihre Antwort eingegeben haben. –

+0

Vielen Dank. Das ist alles was ich frage. Wenn Sie den Mut haben, jemanden zu verärgern, haben Sie den Mut, einen Grund zu nennen. –

1

Ja, aber über eine Schnittstelle bedeuten Sie für jeden Rückruf eine Schnittstelle zu schaffen haben, was bedeutet, jede Funktion, die Sie wollen, dass es passieren gesetzt. Wenn Sie eine Delegate-Klasse erstellen, um das zu handhaben, erhalten Sie (nicht ein echter Funktionszeiger) die übergebene Funktion. Wenn Sie ein generisches Objekt als Rückgabetyp missbrauchen, müssen Sie nicht gießen, was den Engpass auf fast nichts reduziert.

Der C# -Delegat (MultiCastDelegate, um korrekt zu sein) ruft die Informationen aus der Methode MethodInfo ab, die dasselbe ist, was Sie für die Delegate-Klasse mit java.lang.reflect.method tun müssten. Ich habe meinen Code für die Delegate (T) -Klasse auf einem anderen Formular dieser Seite veröffentlicht, das sich mit diesem Problem befasst. Ich mache es, weil (ja) aus C++ kommt Ich brauche eine bessere Möglichkeit zum Übergeben von Funktionen (insbesondere Void), dann muss eine Schnittstelle für eine Funktion oder mehr erstellt werden. Jetzt kann ich die Funktion auswählen, die die Parameterinformation dafür ausfüllt. Voila`! Schön und brauchbar ohne merkliche Geschwindigkeitsverluste von JIT oder JVM. Und wenn ich nur Java-Programmierung nur für eine Woche gelernt habe, kann jeder Java-Programmierer es tun.

Darüber hinaus dient es sehr gut beim Erstellen eines Basis-Listeners und eines Basis-Interface zum Übergeben des Listeners. Sie müssen keinen weiteren Listener mehr schreiben, da sich der Name der Funktion geändert hat. Das Erstellen einer Delegate-Klasse hat große Vorteile, da sie sehr brauchbar und passierbar ist.

0

Sie könnten statische Methoden verbinden. Mit dieser Methode können Sie auch Parameter angeben. Erklären Sie Ihre Schnittstelle ...

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

Und Ihre Karte ...

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

Dann statische Methoden implementieren, die die Schnittstelle/params passen ...

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

Sie können dann diese Methoden in Ihre Karte ...

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

und rufen Sie sie wie folgt ...

RouteHandler handler = routes.get("/foo"); 
handler.handleRequest(exchange);