2010-10-16 7 views
6

(Bitte nicht darauf hinweisen, dass ich soll abstrakt X mehr und eine andere Methode, um es hinzuzufügen.)Java: Kombination von instanceof und cast?

In C++, wenn ich eine Variable x vom Typ habe, und ich will etwas Bestimmtes tun, wenn es Art auch von Y* ist (Y ist eine Unterklasse von X), ich schreibe dies: (? oder ist es)

if(Y* y = dynamic_cast<Y*>(x)) { 
    // now do sth with y 
} 

Das gleiche scheint nicht möglich, in Java.

ich diesen Java-Code stattdessen gelesen haben:

if(x instanceof Y) { 
    Y y = (Y) x; 
    // ... 
} 

Manchmal, wenn Sie es nicht eine Variable x haben, sondern ist ein komplexer Ausdruck stattdessen nur wegen dieses Problems, müssen Sie eine Dummy-Variable in Java:

X x = something(); 
if(x instanceof Y) { 
    Y y = (Y) x; 
    // ... 
} 
// x not needed here anymore 

(Gemeinsame Sache ist, dass something() ist iterator.next() Und da sehen Sie, dass Sie auch nicht wirklich, dass zweimal nur anrufen können Sie wirklich das Dummy-Variable benötigen...)

Sie brauchen nicht wirklich x hier - Sie haben es nur, weil Sie nicht sofort die instanceof Prüfung mit der Besetzung tun können. Vergleichen Sie das wieder auf die recht häufig C++ Code:

if(Y* y = dynamic_cast<Y*>(something())) { 
    // ... 
} 

Aus diesem Grund habe ich eingeführt, um eine castOrNull Funktion, die es ermöglicht, die Dummy-Variable x zu vermeiden. Das kann ich jetzt schreiben:

Y y = castOrNull(something(), Y.class); 
if(y != null) { 
    // ... 
} 

Implementierung von castOrNull:

public static <T> T castOrNull(Object obj, Class<T> clazz) { 
    try { 
     return clazz.cast(obj); 
    } catch (ClassCastException exc) { 
     return null; 
    } 
} 

Nun, ich wurde gesagt, dass using this castOrNull function in that way is an evil thing do to. Warum das? (Oder die Frage stellen allgemein: Würden Sie zustimmen, und denkt auch, dieses Übel ist Wenn ja, warum so Oder denken Sie, dies ein gültiger ist (vielleicht selten) Fall benutzen??)


Wie gesagt Ich möchte keine Diskussion darüber, ob die Verwendung von solchen Downcast überhaupt eine gute Idee ist. Aber lassen Sie mich kurz erklären, warum ich es manchmal verwenden:

  1. Manchmal habe ich in den Fall, wo ich zwischen dem Hinzufügen einer weitere neue Methode für eine ganz bestimmte Sache zu wählen (die nur für eine einzige Unterklasse in einem gelten Fall) oder unter Verwendung einer solchen instanceof Prüfung. Grundsätzlich habe ich die Wahl zwischen dem Hinzufügen einer Funktion doSomethingVeryVerySpecificIfIAmY() oder dem instanceof Check. Und in solchen Fällen fühle ich, dass letzteres sauberer ist.

  2. Manchmal habe ich eine Sammlung von einigen Schnittstelle/Basisklasse und für alle Einträge des Typs Y möchte ich etwas tun und dann aus der Sammlung entfernen. (Zum Beispiel hatte ich den Fall, in dem ich eine Baumstruktur hatte, und ich wollte alle Childs löschen, die leer sind Blätter.)

+1

würde ich statt _class.cast_ verwenden _instanceOf_, um unnötige Ausnahmen in _castOrNull_ zu vermeiden, aber abgesehen davon, dass Sie scheinen völlig normal Helfer zu haben. Lass Tom klarstellen, was er meinte. –

+0

Wenn Y eine _superclass_ von X ist, brauchst du überhaupt keine Besetzung - meinst du _subclass_? –

+0

Während dies in Java kein Einliner ist, ist die Leistung von instanceof und casting ziemlich gut. Ich poste etwas Zeit in Java7 um verschiedene Annäherungen an das Problem hier: http://stackoverflow.com/questions/16320014/java-optimization-nitpick-is-it-faster-to-cast-etwas-und-le-it-it- throw-excep/28858680 # 28858680 – Wheezil

Antwort

8

Jetzt wurde mir gesagt, dass mit dieser Funktion castOrNNull auf diese Weise ist eine böse Sache zu tun. Warum das?

kann ich von ein paar Gründe denken:

  • Es ist eine dunkle und schwierige Art und Weise etwas sehr einfach zu tun. Obskurer und kniffliger Code ist schwer zu lesen, schwer zu pflegen, eine potentielle Fehlerquelle (wenn jemand sie nicht versteht) und daher böse.

  • Die obskure und knifflige Methode, dass die castOrNull-Methode höchstwahrscheinlich funktioniert, kann vom JIT-Compiler nicht optimiert werden. Sie erhalten mindestens 3 zusätzliche Methodenaufrufe sowie viel zusätzlichen Code, um die Typüberprüfung durchzuführen und reflektierend zu wirken. Unnötiger Gebrauch der Reflexion ist böse.

(Dagegen ist die einfache Art und Weise (mit instanceof durch eine Klasse Guss gefolgt) verwendet spezifische Bytecode für instanceof und Klasse Guss. Die Bytecodesequenzen mit ziemlicher Sicherheit optimiert werden kann, so dass es keine mehr als eine null check und nicht mehr als ein Test des Objekttyps im nativen Code Dies ist ein übliches Muster, das der JIT-Compiler leicht erkennen und optimieren sollte.)

Natürlich ist "böse" nur ein anderer Weg sagend, dass du das wirklich nicht tun solltest.

Keines Ihrer beiden zusätzlichen Beispiele, verwenden Sie eine castOrNull Methode entweder notwendig oder wünschenswert. IMO, der "einfache Weg" ist sowohl von der Lesbarkeit als auch von der Leistungsperspektive besser.

+0

Ah, danke für diese Information! Also würdest du auch argumentieren, dass ich 'castOrNull' nicht verwenden soll? Oder wie würdest du diese Kontrapunkte gegen meine gegebenen Pro-Punkte schätzen? (Denn schließlich kann die Vermeidung einer Dummy-Variablen auch zu besser lesbarem und kürzerem Code führen.) – Albert

+1

Der einzige Pro-Punkt, den ich erkennen kann, ist, dass in manchen Fällen die Verwendung einer temporären Variablen vermieden wird. Für mich ist das ein rein kosmetisches Problem. Die Verwendung eines temporären Codes macht den Code nicht zu einem weniger lesbaren IMO, und ein kürzerer Code ist auch kein Lesbarkeitsvorteil. (Und BTW ist es keine "Dummy" Variable, weil es verwendet wird.) –

2

ich nicht genau wissen, warum die Person sagte, dass es böse ist.Eine Möglichkeit für ihre Argumentation war jedoch die Tatsache, dass Sie im Nachhinein eine Ausnahme abgehört haben, anstatt zu überprüfen, bevor Sie gecastet haben. Dies ist eine Möglichkeit, dies zu tun.

public static <T> T castOrNull(Object obj, Class<T> clazz) { 
    if (clazz.isAssignableFrom(obj.getClass())) { 
     return clazz.cast(obj); 
    } else { 
     return null; 
    } 
} 
+0

Ich bezweifle, dass er das meinte, weil ich nach besserem Code fragte und der Code, den ich hier postete, war sein eigener Vorschlag. – Albert

+0

Eine ähnliche Alternative wurde in den Kommentaren zu der verknüpften Antwort diskutiert, und Tom zog es gegenüber der obigen Variante nicht vor. –

+0

@ Albert: verpasst, dass ... da das der Fall ist, würde ich sagen, dass es am wahrscheinlichsten ist, wie Péters Antwort sagt, eine Frage von suboptimalem Design. Aber um wirklich herauszufinden, warum er es falsch findet, muss man auf seine Antwort warten (wenn er jemals wieder zu dir kommt). – Thomas

4

IMHO Ihr castOrNull ist nicht böse, nur sinnlos. Sie scheinen besessen davon zu sein, eine temporäre Variable und eine Codezeile loszuwerden, während für mich die größere Frage ist, warum Sie so viele Downcasts in Ihrem Code benötigen? In OO ist dies fast immer ein Symptom für suboptimales Design. Und ich würde lieber die Grundursache lösen, anstatt das Symptom zu behandeln.

+1

Nun, es gibt viele Bibliotheken in Java, die wir nicht entwerfen, sondern benutzen müssen. (Erwähnte Iterator ist das ein Beispiel) –

+0

@Nikita, fair genug, wenn Sie eine schlecht entworfene aber gleichzeitig unverzichtbare Java-Bibliothek verwenden, haben Sie kaum Optionen. Meine persönliche Vermutung ist, dass solche Bibliotheken sehr selten sein sollten. Wenn eine Bibliothek so umständlich zu benutzen ist, dann benutzt sie niemand oder früher oder später beginnt jemand eine bessere Alternative zu entwickeln. –

+0

Ich habe meine Frage ein wenig erweitert, um 2 Fälle zu zeigen, in denen ich sie manchmal benutze. Ich mag keine harten Regeln, um wirklich ** niemals etwas zu benutzen, und ich denke, dass meine zwei zur Verfügung gestellten Fälle gültige Fälle sind, wo man es so machen könnte. Sie könnten diesbezüglich extremer sein als ich, aber ich denke, Sie wären auch nicht so extrem, niemals Code wie diesen zu verwenden. Und wenn Sie einen solchen Fall haben, möchte ich immer die sauberste Lösung haben und darum habe ich hier gebeten. :) – Albert

5

In den meisten gut geschrieben/entworfen Java-Code die Verwendung von instanceof und wirft nie. Mit der Hinzufügung von Generika werden viele Cases (und damit instanceof) nicht benötigt. Sie tun, gelegentlich noch auftreten.

Die Methode castOrNull ist insofern böse, als Sie Java-Code "unnatürlich" aussehen lassen. Das größte Problem beim Wechsel von einer Sprache in eine andere ist die Übernahme der Konventionen der neuen Sprache. Temporäre Variablen sind in Java gut geeignet. In der Tat ist alles, was Sie tun, die temporäre Variable zu verbergen.

Wenn Sie feststellen, dass Sie eine Menge von Abgüssen schreiben, sollten Sie Ihren Code untersuchen und nach Gründen suchen und nach Möglichkeiten suchen, diese zu entfernen. Wenn Sie zum Beispiel angeben, dass Sie eine "getNumberOfChildren" -Methode hinzufügen, können Sie überprüfen, ob ein Knoten leer ist und ihn somit ohne Casting beschneiden kann (das ist eine Vermutung, in diesem Fall könnte es für Sie nicht funktionieren).

Im Allgemeinen sind Gussformen in Java "böse", weil sie normalerweise nicht benötigt werden. Ihre Methode ist "böser", weil sie nicht so geschrieben ist, wie die meisten Leute erwarten, dass Java geschrieben wird.

Das gesagt, wenn Sie es tun wollen, gehen Sie dafür. Es ist nicht wirklich "böse" nur nicht "richtige" Art, es in Java zu tun.

0

Java-Ausnahmen sind langsam. Wenn Sie versuchen, Ihre Leistung zu optimieren, indem Sie eine Doppelbesetzung vermeiden, schießen Sie sich selbst in den Fuß, indem Sie Ausnahmen anstelle von Logik verwenden. Verlassen Sie sich nie darauf, eine Ausnahme für etwas zu finden, für das Sie nachsehen und korrigieren können (genau das, was Sie tun).

How slow are Java exceptions?