Nachdem ich während meiner C ++ - Tage über die Übel des Cast-Operators im C-Stil unterrichtet worden war, stellte ich zunächst erfreut fest, dass Java 5 java.lang.Class
eine cast
Methode erworben hatte.
Ich dachte, dass wir endlich eine OO-Art haben, mit Casting umzugehen.
Es stellt sich heraus, dass Class.cast
es nicht dasselbe ist wie static_cast
in C ++. Es ist eher so reinterpret_cast
. Es wird kein Kompilierungsfehler generiert, wo er erwartet wird, sondern auf die Laufzeit verschoben. Hier ist ein einfacher Testfall, um verschiedene Verhaltensweisen zu demonstrieren.
package test;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
public class TestCast
{
static final class Foo
{
}
static class Bar
{
}
static final class BarSubclass
extends Bar
{
}
@Test
public void test ( )
{
final Foo foo = new Foo( );
final Bar bar = new Bar( );
final BarSubclass bar_subclass = new BarSubclass( );
{
final Bar bar_ref = bar;
}
{
// Compilation error
final Bar bar_ref = foo;
}
{
// Compilation error
final Bar bar_ref = (Bar) foo;
}
try
{
// !!! Compiles fine, runtime exception
Bar.class.cast( foo );
}
catch ( final ClassCastException ex )
{
assertTrue( true );
}
{
final Bar bar_ref = bar_subclass;
}
try
{
// Compiles fine, runtime exception, equivalent of C++ dynamic_cast
final BarSubclass bar_subclass_ref = (BarSubclass) bar;
}
catch ( final ClassCastException ex )
{
assertTrue( true );
}
}
}
Das sind also meine Fragen.
- Sollte
Class.cast()
nach Generics Land verbannt werden? Dort hat es einige legitime Verwendungszwecke. - Sollten Compiler Kompilierungsfehler erzeugen, wenn sie
Class.cast()
verwendet werden und illegale Bedingungen zur Kompilierungszeit ermittelt werden können? - Sollte Java einen Cast-Operator als Sprachkonstrukt ähnlich wie C ++ bereitstellen?
java
generics
casting
compiler-warnings
Alexander Pogrebnyak
quelle
quelle
Class.cast()
wenn zum Zeitpunkt der Kompilierung illegale Bedingungen festgestellt werden können. In diesem Fall verwenden alle außer Ihnen nur den Standard-Cast-Operator. (3) Java hat einen Cast-Operator als Sprachkonstrukt. Es ist nicht ähnlich wie C ++. Dies liegt daran, dass viele der Java-Sprachkonstrukte C ++ nicht ähnlich sind. Trotz der oberflächlichen Ähnlichkeiten sind Java und C ++ sehr unterschiedlich.Antworten:
Ich habe immer nur
Class.cast(Object)
Warnungen im "Land der Generika" vermieden. Ich sehe oft Methoden, die solche Dinge tun:Es ist oft am besten, es zu ersetzen durch:
Das ist der einzige Anwendungsfall, auf den
Class.cast(Object)
ich jemals gestoßen bin.In Bezug auf Compiler-Warnungen: Ich vermute, dass dies
Class.cast(Object)
nichts Besonderes für den Compiler ist. Es könnte optimiert werden, wenn es statisch verwendet wird (dhFoo.class.cast(o)
nichtcls.cast(o)
), aber ich habe noch nie jemanden gesehen, der es verwendet - was den Aufwand, diese Optimierung in den Compiler zu integrieren, etwas wertlos macht.quelle
Erstens wird dringend davon abgeraten, fast jede Besetzung durchzuführen, daher sollten Sie diese so weit wie möglich einschränken! Sie verlieren die Vorteile der stark typisierten Funktionen von Java zur Kompilierungszeit.
In jedem Fall
Class.cast()
sollte hauptsächlich verwendet werden, wenn Sie dasClass
Token durch Reflektion abrufen . Es ist idiomatischer zu schreibeneher, als
BEARBEITEN: Fehler beim Kompilieren
Insgesamt führt Java Cast-Checks nur zur Laufzeit durch. Der Compiler kann jedoch einen Fehler ausgeben, wenn er nachweisen kann, dass solche Casts niemals erfolgreich sein können (z. B. eine Klasse in eine andere Klasse umwandeln, die kein Supertyp ist, und einen endgültigen Klassentyp in eine Klasse / Schnittstelle umwandeln, die nicht in ihrer Typhierarchie enthalten ist). Hier , da
Foo
undBar
sind Klassen , die nicht in jeder anderen Hierarchie sind, kann die Besetzung nie gelingen.quelle
Class.cast
mehr Probleme zu bereiten scheint, als es zu lösen scheint.Es ist immer problematisch und oft irreführend, Konstrukte und Konzepte zwischen Sprachen zu übersetzen. Casting ist keine Ausnahme. Insbesondere, weil Java eine dynamische Sprache ist und C ++ etwas anders ist.
Alle Castings in Java, egal wie Sie es machen, werden zur Laufzeit durchgeführt. Typinformationen werden zur Laufzeit gespeichert. C ++ ist eher eine Mischung. Sie können eine Struktur in C ++ in eine andere umwandeln und es handelt sich lediglich um eine Neuinterpretation der Bytes, die diese Strukturen darstellen. Java funktioniert so nicht.
Auch Generika in Java und C ++ sind sehr unterschiedlich. Kümmere dich nicht zu sehr darum, wie du C ++ in Java machst. Sie müssen lernen, wie man Dinge auf Java-Art macht.
quelle
(Bar) foo
entnehmen können, wird beim Kompilieren zwar ein Fehler generiert, dies jedochBar.class.cast(foo)
nicht. Meiner Meinung nach sollte es so verwendet werden.Bar.class.cast(foo)
teilt dem Compiler explizit mit, dass Sie die Umwandlung zur Laufzeit durchführen möchten. Wenn Sie die Gültigkeit der Besetzung zur Kompilierungszeit überprüfen möchten, können Sie nur die(Bar) foo
Stilumwandlung durchführen.Class.cast()
wird im Java-Code selten verwendet. Wenn es verwendet wird, dann normalerweise mit Typen, die nur zur Laufzeit bekannt sind (dh über ihre jeweiligenClass
Objekte und durch einen Typparameter). Es ist nur in Code wirklich nützlich, der Generika verwendet (das ist auch der Grund, warum es nicht früher eingeführt wurde).Es ist nicht ähnlich wie
reinterpret_cast
, weil es Ihnen nicht erlaubt, das Typsystem zur Laufzeit mehr zu brechen als eine normale Besetzung (dh Sie können generische Typparameter brechen , aber keine "echten" Typen brechen ).Die Übel des Cast-Operators im C-Stil gelten im Allgemeinen nicht für Java. Der Java-Code, der wie eine Besetzung im C-Stil aussieht, ähnelt am ehesten einem
dynamic_cast<>()
mit einem Referenztyp in Java (denken Sie daran: Java verfügt über Laufzeittypinformationen).Im Allgemeinen ist der Vergleich der C ++ - Casting-Operatoren mit dem Java-Casting ziemlich schwierig, da Sie in Java immer nur Referenzen umwandeln können und keine Konvertierung von Objekten erfolgt (nur primitive Werte können mit dieser Syntax konvertiert werden).
quelle
dynamic_cast<>()
mit einem Referenztyp.Im Allgemeinen wird der Cast-Operator der Class # Cast-Methode vorgezogen, da er präziser ist und vom Compiler analysiert werden kann, um offensichtliche Probleme mit dem Code auszuspucken.
Class # cast übernimmt die Verantwortung für die Typprüfung zur Laufzeit und nicht während der Kompilierung.
Es gibt sicherlich Anwendungsfälle für Class # Cast, insbesondere wenn es um reflektierende Operationen geht.
Seit Lambda zu Java gekommen ist, verwende ich persönlich gerne Class # cast mit der Sammlungen / Stream-API, wenn ich zum Beispiel mit abstrakten Typen arbeite.
quelle
C ++ und Java sind verschiedene Sprachen.
Der Cast-Operator im Java C-Stil ist wesentlich eingeschränkter als die C / C ++ - Version. Tatsächlich ähnelt die Java-Umwandlung der C ++ - Dynamic_cast. Wenn das Objekt, das Sie haben, nicht in die neue Klasse umgewandelt werden kann, wird eine Laufzeitausnahme angezeigt (oder wenn im Code genügend Informationen zur Kompilierungszeit vorhanden sind). Daher ist die C ++ - Idee, keine Casts vom Typ C zu verwenden, in Java keine gute Idee
quelle
Zusätzlich zum Entfernen hässlicher Cast-Warnungen, wie am häufigsten erwähnt, ist Class.cast ein Laufzeit-Cast, der hauptsächlich für generisches Casting verwendet wird. Generische Informationen werden zur Laufzeit gelöscht und einige, wie jedes generische Objekt betrachtet wird, führen dazu, dass dies nicht der Fall ist eine frühe ClassCastException auslösen.
Verwenden Sie diesen Trick beispielsweise beim Erstellen der Objekte. Überprüfen Sie S p = service.cast (c.newInstance ()). Dies löst eine Klassenumwandlungsausnahme aus, wenn SP = (S) c.newInstance (); wird und wird möglicherweise keine Warnung ' Typensicherheit: Deaktivierte Umwandlung von Objekt nach S' anzeigen (wie Objekt P = (Objekt) c.newInstance ();)
- Es wird einfach überprüft, ob das gegossene Objekt eine Instanz der Besetzungsklasse ist, und es wird der Besetzungsoperator verwendet, um die Warnung durch Unterdrücken umzuwandeln und auszublenden.
Java-Implementierung für dynamische Besetzung:
quelle
Persönlich habe ich dies bereits verwendet, um einen JSON-POJO-Konverter zu erstellen. Für den Fall, dass das mit der Funktion verarbeitete JSONObject ein Array oder verschachtelte JSONObjects enthält (was bedeutet, dass die Daten hier nicht vom primitiven Typ sind oder
String
), versuche ich, die Setter-Methode folgendermaßen aufzurufenclass.cast()
:class.cast()
Ich bin mir nicht sicher, ob dies äußerst hilfreich ist, aber wie hier bereits erwähnt, ist Reflexion einer der wenigen legitimen Anwendungsfälle, an die ich denken kann. Zumindest haben Sie jetzt ein anderes Beispiel.quelle