Beachten Sie, dass der Grund, warum Raw-Typen existieren, die Abwärtskompatibilität mit Java 1.4 und älteren Versionen ist, für die es überhaupt keine Generika gab.
Jesper
Antworten:
744
Was ist ein Rohtyp?
Die Java-Sprachspezifikation definiert einen Rohtyp wie folgt:
Der Referenztyp, der gebildet wird, indem der Name einer generischen Typdeklaration ohne zugehörige Typargumentliste verwendet wird.
Ein Array-Typ, dessen Elementtyp ein Rohtyp ist.
Ein Nicht- staticMitgliedstyp eines Rohtyps R, der nicht von einer Oberklasse oder Superschnittstelle von geerbt wird R.
Hier ist ein Beispiel zur Veranschaulichung:
publicclassMyType<E>{classInner{}staticclassNested{}publicstaticvoid main(String[] args){MyType mt;// warning: MyType is a raw typeMyType.Inner inn;// warning: MyType.Inner is a raw typeMyType.Nested nest;// no warning: not parameterized typeMyType<Object> mt1;// no warning: type parameter givenMyType<?> mt2;// no warning: type parameter given (wildcard OK!)}}
Hier MyType<E>ist ein parametrisierter Typ ( JLS 4.5 ). Es ist üblich, diesen Typ umgangssprachlich als MyTypekurz zu bezeichnen, aber technisch ist der Name MyType<E>.
mthat einen Rohtyp (und generiert eine Kompilierungswarnung) durch den ersten Aufzählungspunkt in der obigen Definition; innhat auch einen rohen Typ durch den dritten Aufzählungspunkt.
MyType.Nestedist kein parametrisierter Typ, obwohl es sich um einen Elementtyp eines parametrisierten Typs handelt MyType<E>, weil dies der Fall ist static.
mt1und mt2werden beide mit tatsächlichen Typparametern deklariert, sodass es sich nicht um Rohtypen handelt.
Was ist das Besondere an rohen Sorten?
Im Wesentlichen verhalten sich Rohtypen genauso wie vor der Einführung von Generika. Das heißt, Folgendes ist zur Kompilierungszeit völlig legal.
List names =newArrayList();// warning: raw type!
names.add("John");
names.add("Mary");
names.add(Boolean.FALSE);// not a compilation error!
Der obige Code läuft einwandfrei, aber nehmen wir an, Sie haben auch Folgendes:
for(Object o : names){String name =(String) o;System.out.println(name);}// throws ClassCastException!// java.lang.Boolean cannot be cast to java.lang.String
Jetzt stoßen wir zur Laufzeit auf Probleme, weil sie namesetwas enthalten, das kein ist instanceof String.
Vermutlich , wenn Sie wollen namesnur enthalten String, Sie könnten vielleicht noch eine rohe Art verwenden und manuell jeden überprüfenadd sich selbst, und dann manuell geworfen zu Stringjedem Element aus names. Noch besser ist es jedoch , KEINEN Rohtyp zu verwenden und den Compiler die ganze Arbeit für Sie erledigen zu lassen , wobei die Leistung von Java-Generika genutzt wird.
Wie unterscheidet sich ein Rohtyp von der Verwendung <Object>als Typparameter?
Das Folgende ist ein Zitat aus Effective Java 2nd Edition, Punkt 23: Verwenden Sie keine Rohtypen in neuem Code :
Was ist der Unterschied zwischen dem Rohtyp Listund dem parametrisierten Typ List<Object>? Ersteres hat die generische Typprüfung deaktiviert, während letzteres dem Compiler ausdrücklich mitteilte, dass er Objekte aller Art aufnehmen kann. Während Sie a List<String>an einen Parameter vom Typ übergeben können List, können Sie es nicht an einen Parameter vom Typ übergeben List<Object>. Es gibt Subtypisierungsregeln für Generika und es List<String>handelt sich um einen Subtyp des Rohtyps List, jedoch nicht des parametrisierten Typs List<Object>. Infolgedessen verlieren Sie die Typensicherheit, wenn Sie einen rohen Typ wie verwenden List, aber nicht, wenn Sie einen parametrisierten Typ wie verwendenList<Object> .
Betrachten Sie zur Veranschaulichung des Punktes die folgende Methode, die a nimmt List<Object>und a anfügt new Object().
Wenn Sie deklariert hätten appendNewObject, einen Rohtyp Listals Parameter zu verwenden, würde dies kompiliert, und Sie würden daher die Typensicherheit verlieren, die Sie von Generika erhalten.
Wie unterscheidet sich ein Rohtyp von der Verwendung <?>als Typparameter?
List<Object>, List<String>Usw. sind alle List<?>, so dass es nur verlockend sein sagen , dass sie gerade sind , Liststatt. Es gibt jedoch einen großen Unterschied: Da nur a List<E>definiert ist add(E), können Sie einem nicht jedes beliebige Objekt hinzufügenList<?> . Auf der anderen Seite, da der Rohtyp Listkeine Typensicherheit hat, können Sie fast addalles zu einem List.
Betrachten Sie die folgende Variation des vorherigen Snippets:
staticvoid appendNewObject(List<?> list){
list.add(newObject());// compilation error!}//...List<String> names =newArrayList<String>();
appendNewObject(names);// this part is fine!
Der Compiler hat Sie wunderbar davor geschützt, die Typinvarianz von List<?>! Wenn Sie den Parameter als Rohtyp deklariert habenList list hätten, würde der Code kompiliert und Sie würden die Typinvariante von verletzen List<String> names.
Ein Rohtyp ist das Löschen dieses Typs
Zurück zu JLS 4.8:
Es ist möglich, die Löschung eines parametrisierten Typs oder die Löschung eines Array-Typs als Typ zu verwenden, dessen Elementtyp ein parametrisierter Typ ist. Ein solcher Typ wird als Rohtyp bezeichnet .
[...]
Die Superklassen (bzw. Superschnittstellen) eines Rohtyps sind die Löschungen der Superklassen (Superschnittstellen) einer der Parametrisierungen des generischen Typs.
Der Typ eines Konstruktors, einer Instanzmethode oder eines Nichtfelds staticeines Rohtyps C, der nicht von seinen Oberklassen oder Superschnittstellen geerbt wird, ist der Rohtyp, der dem Löschen seines Typs in der entsprechenden generischen Deklaration entspricht C.
Einfacher ausgedrückt, wenn ein Roh - Typ verwendet wird, die Konstrukteure, Instanzmethoden und nicht staticsind Felder ebenfalls gelöscht .
Beim Löschen von Typen wird auch die Signatur eines Konstruktors oder einer Methode einer Signatur zugeordnet, die keine parametrisierten Typen oder Typvariablen enthält. Das Löschen einer Konstruktor- oder Methodensignatur sist eine Signatur, die aus demselben Namen wie sund den Löschungen aller in angegebenen formalen Parametertypen besteht s.
Der Rückgabetyp einer Methode und die Typparameter einer generischen Methode oder eines generischen Konstruktors werden ebenfalls gelöscht, wenn die Signatur der Methode oder des Konstruktors gelöscht wird.
Das Löschen der Signatur einer generischen Methode hat keine Typparameter.
Der folgende Fehlerbericht enthält einige Gedanken von Maurizio Cimadamore, einem Compiler-Entwickler, und Alex Buckley, einem der Autoren des JLS, warum dieses Verhalten auftreten sollte: https://bugs.openjdk.java.net/browse / JDK-6400189 . (Kurz gesagt, dies vereinfacht die Spezifikation.)
Wenn es unsicher ist, warum darf es einen Rohtyp verwenden?
Hier ist ein weiteres Zitat aus JLS 4.8:
Die Verwendung von Rohtypen ist nur als Zugeständnis an die Kompatibilität von Legacy-Code zulässig. Von der Verwendung von Rohtypen in Code, der nach Einführung der Generizität in die Programmiersprache Java geschrieben wurde, wird dringend abgeraten. Es ist möglich, dass zukünftige Versionen der Programmiersprache Java die Verwendung von Rohtypen nicht zulassen.
Effektive Java 2nd Edition hat auch Folgendes hinzuzufügen:
Warum haben die Sprachdesigner sie zugelassen, da Sie keine Rohtypen verwenden sollten? Kompatibilität bieten.
Die Java-Plattform stand kurz vor dem Eintritt in das zweite Jahrzehnt, als Generika eingeführt wurden, und es gab eine enorme Menge an Java-Code, der keine Generika verwendete. Es wurde als kritisch erachtet, dass all dieser Code legal bleibt und mit neuem Code kompatibel ist, der Generika verwendet. Es musste legal sein, Instanzen parametrisierter Typen an Methoden zu übergeben, die für die Verwendung mit normalen Typen entwickelt wurden, und umgekehrt. Diese als Migrationskompatibilität bekannte Anforderung führte zur Entscheidung, Rohtypen zu unterstützen.
Zusammenfassend sollte der Rohtyp NIEMALS in neuem Code verwendet werden. Sie sollten immer parametrisierte Typen verwenden .
Gibt es keine Ausnahmen?
Da Java-Generika nicht reifiziert sind, gibt es leider zwei Ausnahmen, in denen Rohtypen in neuem Code verwendet werden müssen:
Was meinst du damit, "Java-Generika sind nicht reifiziert"?
Carl G
7
Für die zweite Ausnahme o instanceof Set<?>darf die Syntax auch den Rohtyp vermeiden (obwohl dies in diesem Fall nur oberflächlich ist).
Paul Bellora
1
Raw-Typen sind sehr nützlich und reduzieren den Boilerplate-Code bei JNDI-Lookups für eine Bean, die eine Schnittstelle erweitert. Dadurch wird die Notwendigkeit behoben, nRemote-Beans für jede implementierende Klasse mit identischem Code zu schreiben .
DJJJ
8
"Nicht reifiziert" ist eine andere Art zu sagen, dass sie gelöscht werden. Der Compiler kennt die generischen Parameter, diese Informationen werden jedoch nicht an den generierten Bytecode weitergegeben. Das JLS erfordert, dass Klassenliterale keine Typparameter haben.
Erick G. Hagstrom
2
@ OldCurmudgeon Das ist interessant. Ich meine, offiziell ist es auch nicht , weil ein Klassenliteral als gerecht definiert ist TypeName.class, wo TypeNameein einfacher Bezeichner ( jls ) ist. Hypothetisch gesehen könnte es auch wirklich so sein. Vielleicht als Hinweis List<String>.classist die Variante, die das JLS speziell als Compilerfehler bezeichnet. Wenn sie ihn also jemals der Sprache hinzufügen, würde ich erwarten, dass dies diejenige ist, die sie verwenden.
Radiodef
62
Was sind Raw-Typen in Java und warum höre ich oft, dass sie nicht in neuem Code verwendet werden sollten?
Rohtypen sind eine alte Geschichte der Java-Sprache. Am Anfang gab es Collectionsund sie hielten Objectsnicht mehr und nicht weniger. Jede Operation an Collectionserforderlichen Abgüssen von Objectbis zum gewünschten Typ.
List aList =newArrayList();String s ="Hello World!";
aList.add(s);String c =(String)aList.get(0);
Während dies die meiste Zeit funktionierte, traten Fehler auf
List aNumberList =newArrayList();String one ="1";//Number one
aNumberList.add(one);Integer iOne =(Integer)aNumberList.get(0);//Insert ClassCastException here
Die alten typlosen Sammlungen konnten die Typensicherheit nicht erzwingen, sodass sich der Programmierer daran erinnern musste, was er in einer Sammlung gespeichert hatte.
Generika wurden erfunden, um diese Einschränkung zu umgehen. Der Entwickler deklarierte den gespeicherten Typ einmal und der Compiler tat dies stattdessen.
List<String> aNumberList =newArrayList<String>();
aNumberList.add("one");Integer iOne = aNumberList.get(0);//Compile time errorString sOne = aNumberList.get(0);//works fine
Zum Vergleich:
// Old style collections now known as raw typesList aList =newArrayList();//Could contain anything// New style collections with GenericsList<String> aList =newArrayList<String>();//Contains only Strings
Komplexer die vergleichbare Oberfläche:
//raw, not type save can compare with Other classesclassMyCompareAbleimplementsCompareAble{int id;publicint compareTo(Object other){returnthis.id -((MyCompareAble)other).id;}}//GenericclassMyCompareAbleimplementsCompareAble<MyCompareAble>{int id;publicint compareTo(MyCompareAble other){returnthis.id - other.id;}}
Beachten Sie, dass es nicht möglich ist, die CompareAbleSchnittstelle mit zu implementierencompareTo(MyCompareAble) mit Raw-Typen . Warum sollten Sie sie nicht verwenden:
Irgendein Object in a gespeicherten Collectionmüssen gegossen werden, bevor sie verwendet werden können
Die Verwendung von Generika ermöglicht die Überprüfung der Kompilierungszeit
Die Verwendung von Rohtypen entspricht dem Speichern jedes Werts als Object
Was der Compiler macht: Generika sind abwärtskompatibel, sie verwenden dieselben Java-Klassen wie die Raw-Typen. Die Magie geschieht meistens zur Kompilierungszeit.
List<String> someStrings =newArrayList<String>();
someStrings.add("one");String one = someStrings.get(0);
Wird zusammengestellt als:
List someStrings =newArrayList();
someStrings.add("one");String one =(String)someStrings.get(0);
Dies ist derselbe Code, den Sie schreiben würden, wenn Sie die Rohtypen direkt verwenden würden. Obwohl ich nicht sicher bin, was mit der CompareAbleSchnittstelle passiert , werden vermutlich zwei compareToFunktionen erstellt, von denen eine eine MyCompareAbleund die andere eine übernimmt Objectund sie nach dem Casting an die erste weitergibt.
Was sind die Alternativen zu Rohtypen: Verwenden Sie Generika
Um einen parametrisierten Typ von zu erstellen Box<T>, geben Sie ein tatsächliches Typargument für den formalen Typparameter an T:
Box<Integer> intBox =newBox<>();
Wenn das eigentliche Typargument weggelassen wird, erstellen Sie einen Rohtyp von Box<T>:
Box rawBox =newBox();
Daher Boxist der Rohtyp des generischen Typs Box<T>. Ein nicht generischer Klassen- oder Schnittstellentyp ist jedoch kein Rohtyp.
Raw-Typen werden im Legacy-Code angezeigt, da viele API-Klassen (wie die Collections-Klassen) vor JDK 5.0 nicht generisch waren. Wenn Sie Raw-Typen verwenden, erhalten Sie im Wesentlichen ein Verhalten vor Generika - a Boxgibt Ihnen Objects. Aus Gründen der Abwärtskompatibilität ist es zulässig, seinem Rohtyp einen parametrisierten Typ zuzuweisen:
Box<String> stringBox =newBox<>();Box rawBox = stringBox;// OK
Wenn Sie jedoch einem parametrisierten Typ einen Rohtyp zuweisen, erhalten Sie eine Warnung:
Box rawBox =newBox();// rawBox is a raw type of Box<T>Box<Integer> intBox = rawBox;// warning: unchecked conversion
Sie erhalten auch eine Warnung, wenn Sie einen Rohtyp verwenden, um generische Methoden aufzurufen, die im entsprechenden generischen Typ definiert sind:
Die Warnung zeigt, dass Rohtypen generische Typprüfungen umgehen und den Fang von unsicherem Code auf die Laufzeit verschieben. Daher sollten Sie die Verwendung von Rohtypen vermeiden.
Im Abschnitt Typlöschung finden Sie weitere Informationen zur Verwendung von Rohtypen durch den Java-Compiler.
Deaktivierte Fehlermeldungen
Wie bereits erwähnt, können beim Mischen von Legacy-Code mit generischem Code Warnmeldungen auftreten, die den folgenden ähneln:
Hinweis: Example.java verwendet nicht aktivierte oder unsichere Vorgänge.
Hinweis: Mit -Xlint neu kompilieren: Für Details deaktiviert.
Dies kann passieren, wenn eine ältere API verwendet wird, die mit Rohtypen arbeitet, wie im folgenden Beispiel gezeigt:
publicclassWarningDemo{publicstaticvoid main(String[] args){Box<Integer> bi;
bi = createBox();}staticBox createBox(){returnnewBox();}}
Der Begriff "nicht markiert" bedeutet, dass der Compiler nicht über genügend Typinformationen verfügt, um alle Typprüfungen durchzuführen, die zur Gewährleistung der Typensicherheit erforderlich sind. Die Warnung "nicht markiert" ist standardmäßig deaktiviert, obwohl der Compiler einen Hinweis gibt. Um alle "nicht aktivierten" Warnungen anzuzeigen, kompilieren Sie erneut mit -Xlint: nicht aktiviert.
Wenn Sie das vorherige Beispiel mit -Xlint neu kompilieren: Wenn diese Option nicht aktiviert ist, werden die folgenden zusätzlichen Informationen angezeigt:
WarningDemo.java:4: warning:[unchecked] unchecked conversion
found :Box
required:Box<java.lang.Integer>
bi = createBox();^1 warning
Verwenden Sie das Flag -Xlint: -unchecked, um ungeprüfte Warnungen vollständig zu deaktivieren. Die @SuppressWarnings("unchecked")Anmerkung unterdrückt ungeprüfte Warnungen. Wenn Sie mit dem nicht vertraut sind@SuppressWarnings Syntax nicht Anmerkungen.
Ein "roher" Typ in Java ist eine Klasse, die nicht generisch ist und sich eher mit "rohen" Objekten als mit typsicheren generischen Typparametern befasst.
Bevor beispielsweise Java-Generika verfügbar waren, haben Sie eine Auflistungsklasse wie die folgende verwendet:
LinkedList list =newLinkedList();
list.add(newMyObject());MyObject myObject =(MyObject)list.get(0);
Wenn Sie Ihr Objekt zur Liste hinzufügen, ist es egal, um welchen Objekttyp es sich handelt, und wenn Sie es aus der Liste erhalten, müssen Sie es explizit in den erwarteten Typ umwandeln.
Mit Generika entfernen Sie den "unbekannten" Faktor, da Sie explizit angeben müssen, welcher Objekttyp in die Liste aufgenommen werden kann:
LinkedList<MyObject> list =newLinkedList<MyObject>();
list.add(newMyObject());MyObject myObject = list.get(0);
Beachten Sie, dass Sie bei Generika das aus dem Aufruf get stammende Objekt nicht umwandeln müssen. Die Auflistung ist vordefiniert, um nur mit MyObject zu funktionieren. Diese Tatsache ist der Hauptantriebsfaktor für Generika. Es ändert eine Quelle von Laufzeitfehlern in etwas, das zur Kompilierungszeit überprüft werden kann.
Insbesondere erhalten Sie einen Rohtyp, wenn Sie die Typparameter für einen generischen Typ einfach weglassen. Raw-Typen waren eigentlich immer nur eine Abwärtskompatibilitätsfunktion und können möglicherweise entfernt werden. Sie können ein ähnliches Verhalten mit bekommen? Platzhalterparameter.
John Flatness
@zerocrates: ähnlich aber anders! Die Verwendung ?bietet immer noch Typensicherheit. Ich habe es in meiner Antwort behandelt.
Polygenschmierstoffe
19
privatestaticList<String> list =newArrayList<String>();
Sie sollten den Typparameter angeben.
In der Warnung wird darauf hingewiesen, dass Typen unterstützt werden sollen Generika zu parametrisieren, anstatt ihre Rohform zu verwenden.
Listist definiert, um Generika zu unterstützen : public class List<E>. Dies ermöglicht viele typsichere Operationen, die zur Kompilierungszeit überprüft werden.
Jetzt ersetzt durch Diamant-Inferenz in Java 7 -private static List<String> list = new ArrayList<>();
Ian Campbell
14
Was ist ein Rohtyp und warum höre ich oft, dass sie nicht in neuem Code verwendet werden sollten?
Ein "Rohtyp" ist die Verwendung einer generischen Klasse, ohne ein oder mehrere Typargumente für ihre parametrisierten Typen anzugeben, z . B. Listanstelle von List<String>. Bei der Einführung von Generika in Java wurden mehrere Klassen aktualisiert, um Generika zu verwenden. Durch die Verwendung dieser Klasse als "Rohtyp" (ohne Angabe eines Typarguments) konnte der Legacy-Code weiterhin kompiliert werden.
"Raw-Typen" werden aus Gründen der Abwärtskompatibilität verwendet. Ihre Verwendung in neuem Code wird nicht empfohlen, da die Verwendung der generischen Klasse mit einem Typargument eine stärkere Typisierung ermöglicht, was wiederum die Verständlichkeit des Codes verbessern und dazu führen kann, dass potenzielle Probleme früher erkannt werden.
Was ist die Alternative, wenn wir keine Rohtypen verwenden können, und wie ist es besser?
Die bevorzugte Alternative besteht darin, generische Klassen wie beabsichtigt zu verwenden - mit einem geeigneten Typargument (z List<String> . ). Auf diese Weise kann der Programmierer Typen genauer angeben, zukünftigen Betreuern mehr Bedeutung für die beabsichtigte Verwendung einer Variablen oder Datenstruktur vermitteln und der Compiler kann eine bessere Typensicherheit erzwingen. Diese Vorteile zusammen können die Codequalität verbessern und dazu beitragen, die Einführung einiger Codierungsfehler zu verhindern.
Beispiel: Bei einer Methode, bei der der Programmierer sicherstellen möchte, dass eine List-Variable mit dem Namen "names" nur Strings enthält:
List<String> names =newArrayList<String>();
names.add("John");// OK
names.add(newInteger(1));// compile error
Ah, ich bin so versucht, polygenelubricantsdie "Raw Type" -Referenzen von stackoverflow.com/questions/2770111/… in meine eigene Antwort zu kopieren , aber ich nehme an, ich lasse sie für seine / ihre eigene Antwort.
Bert F
1
Ja, ich habe dieses Segment im Wesentlichen überall dort kopiert und eingefügt, wo Menschen Raw-Typen für den Stackoverflow verwenden, und mich schließlich entschlossen, von nun an nur noch eine Frage zu stellen. Ich hoffe, es ist ein guter Beitrag für die Community.
Polygenschmierstoffe
1
@polygenelubricants Ich habe bemerkt - wir haben einige der gleichen Fragen gestellt :-)
Bert F
1
@ ha9u63ar: In der Tat. Im Allgemeinen sind prägnante und einfache Antworten mindestens so gut wie die langen und akzeptierten.
DisplayName
Was ist "stärkeres Syping"?
Carloswm85
12
Der Compiler möchte, dass Sie Folgendes schreiben:
privatestaticList<String> list =newArrayList<String>();
Andernfalls können Sie einen beliebigen Typ hinzufügen list, wodurch die Instanziierung new ArrayList<String>()sinnlos wird. Java-Generika sind nur eine Funktion zur Kompilierungszeit, daher new ArrayList<String>()akzeptiert ein Objekt, das mit erstellt wurde , Elemente Integeroder JFrameElemente, wenn sie einer Referenz des "Rohtyps" zugewiesen sind. ListDas Objekt selbst weiß nichts darüber, welche Typen es enthalten soll, nur der Compiler.
ArrayList<String> arrEs ist eine ArrayListReferenzvariable mit Typ, Stringdie auf ein ArralyListObjekt vom Typ verweist String. Dies bedeutet, dass es nur Objekte vom Typ String enthalten kann.
Es ist streng, Stringkein Rohtyp zu sein, daher wird niemals eine Warnung ausgegeben.
arr.add("hello");// alone statement will compile successfully and no warning.
arr.add(23);//prone to compile time error.//error: no suitable method found for add(int)
Fall 2
In diesem Fall ArrayList<String> arrhandelt es sich um einen strengen Typ, aber Ihr Objekt new ArrayList();ist ein Rohtyp.
arr.add("hello");//alone this compile but raise the warning.
arr.add(23);//again prone to compile time error.//error: no suitable method found for add(int)
Hier arrist ein strenger Typ. Daher wird beim Hinzufügen von a ein Fehler bei der Kompilierungszeit ausgelöst integer.
Warnung : - Ein RawTypobjekt wird auf eine StrictTypreferenzvariable von verwiesen ArrayList.
Fall 3
In diesem Fall ArrayList arrhandelt es sich um einen Rohtyp, aber Ihr Objekt new ArrayList<String>();ist ein strikter Typ.
arr.add("hello");
arr.add(23);//compiles fine but raise the warning.
Es wird jeder Objekttyp hinzugefügt, da arres sich um einen Raw-Typ handelt.
Warnung : - Ein StrictTypobjekt wird auf eine rawtypbezogene Variable verwiesen.
Ein Raw- Typ ist das Fehlen eines Typparameters bei Verwendung eines generischen Typs.
Der Raw-Typ sollte nicht verwendet werden, da dies zu Laufzeitfehlern führen kann, z. B. zum Einfügen eines doublein einen Setvon ints.
Set set =newHashSet();
set.add(3.45);//ok
Wenn Sie das Zeug aus dem Setabrufen, wissen Sie nicht, was herauskommt. Nehmen wir an, Sie erwarten, dass alles ints ist, auf das Sie es werfen Integer. Ausnahme zur Laufzeit, wenn die double3.45 kommt.
Wenn Sie einen Typparameter hinzufügen Set, wird sofort ein Kompilierungsfehler angezeigt. Mit diesem vorbeugenden Fehler können Sie das Problem beheben, bevor zur Laufzeit etwas in die Luft sprengt (wodurch Zeit und Mühe gespart werden).
Set<Integer> set =newHashSet<Integer>();
set.add(3.45);//NOT ok.
Wie in der akzeptierten Antwort erwähnt, verlieren Sie jegliche Unterstützung für Generika innerhalb des Codes des Rohtyps. Jeder Typparameter wird in seine Löschung konvertiert (was im obigen Beispiel nur der Fall ist Object).
Was ist zu sagen ist , dass Ihr listeine ist Listvon unespecified Objekte. Das heißt, Java weiß nicht, welche Art von Objekten sich in der Liste befinden. Wenn Sie dann die Liste iterieren möchten, müssen Sie jedes Element umwandeln, um auf die Eigenschaften dieses Elements zugreifen zu können (in diesem Fall String).
Im Allgemeinen ist es eine bessere Idee, die Sammlungen zu parametrisieren, damit Sie keine Konvertierungsprobleme haben. Sie können nur Elemente des parametrisierten Typs hinzufügen und Ihr Editor bietet Ihnen die geeigneten Methoden zur Auswahl an.
privatestaticList<String> list =newArrayList<String>();
Rohtypen beziehen sich auf die Verwendung eines generischen Typs ohne Angabe eines Typparameters.
Zum Beispiel ,
Eine Liste ist ein Rohtyp, während List<String>es sich um einen parametrisierten Typ handelt.
Bei der Einführung von Generika in JDK 1.5 wurden Raw-Typen nur beibehalten, um die Abwärtskompatibilität mit älteren Java-Versionen zu gewährleisten. Obwohl die Verwendung von Rohtypen weiterhin möglich ist,
Sie sollten vermieden werden :
Sie erfordern normalerweise Abgüsse
Sie sind nicht typsicher und einige wichtige Arten von Fehlern werden nur zur Laufzeit angezeigt
Sie sind weniger ausdrucksvoll , und nicht selbst Dokument in der gleichen Weise wie parametrisierte Typen
Beispiel
import java.util.*;publicfinalclassAvoidRawTypes{void withRawType(){//Raw List doesn't self-document, //doesn't state explicitly what it can containList stars =Arrays.asList("Arcturus","Vega","Altair");Iterator iter = stars.iterator();while(iter.hasNext()){String star =(String) iter.next();//cast needed
log(star);}}void withParameterizedType(){List<String> stars =Arrays.asList("Spica","Regulus","Antares");for(String star: stars){
log(star);}}privatevoid log(Object message){System.out.println(Objects.toString(message));}}
Ich fand diese Seite, nachdem ich einige Beispielübungen gemacht hatte und genau die gleiche Verwirrung hatte.
============== Ich ging von diesem Code aus, wie im Beispiel angegeben ===============
publicstaticvoid main(String[] args)throwsIOException{Map wordMap =newHashMap();if(args.length >0){for(int i =0; i < args.length; i++){
countWord(wordMap, args[i]);}}else{
getWordFrequency(System.in, wordMap);}for(Iterator i = wordMap.entrySet().iterator(); i.hasNext();){Map.Entry entry =(Map.Entry) i.next();System.out.println(entry.getKey()+" :\t"+ entry.getValue());}
===================== Zu diesem Code ========================
publicstaticvoid main(String[] args)throwsIOException{// replace with TreeMap to get them sorted by nameMap<String,Integer> wordMap =newHashMap<String,Integer>();if(args.length >0){for(int i =0; i < args.length; i++){
countWord(wordMap, args[i]);}}else{
getWordFrequency(System.in, wordMap);}for(Iterator<Entry<String,Integer>> i = wordMap.entrySet().iterator(); i.hasNext();){Entry<String,Integer> entry = i.next();System.out.println(entry.getKey()+" :\t"+ entry.getValue());}}
Rohe Typen sind in Ordnung, wenn sie ausdrücken, was Sie ausdrücken möchten.
Beispielsweise kann eine Deserialisierungsfunktion a zurückgeben List, kennt jedoch den Elementtyp der Liste nicht. So Listist hier der passende Rückgabetyp.
Antworten:
Was ist ein Rohtyp?
Die Java-Sprachspezifikation definiert einen Rohtyp wie folgt:
JLS 4.8 Rohtypen
Hier ist ein Beispiel zur Veranschaulichung:
Hier
MyType<E>
ist ein parametrisierter Typ ( JLS 4.5 ). Es ist üblich, diesen Typ umgangssprachlich alsMyType
kurz zu bezeichnen, aber technisch ist der NameMyType<E>
.mt
hat einen Rohtyp (und generiert eine Kompilierungswarnung) durch den ersten Aufzählungspunkt in der obigen Definition;inn
hat auch einen rohen Typ durch den dritten Aufzählungspunkt.MyType.Nested
ist kein parametrisierter Typ, obwohl es sich um einen Elementtyp eines parametrisierten Typs handeltMyType<E>
, weil dies der Fall iststatic
.mt1
undmt2
werden beide mit tatsächlichen Typparametern deklariert, sodass es sich nicht um Rohtypen handelt.Was ist das Besondere an rohen Sorten?
Im Wesentlichen verhalten sich Rohtypen genauso wie vor der Einführung von Generika. Das heißt, Folgendes ist zur Kompilierungszeit völlig legal.
Der obige Code läuft einwandfrei, aber nehmen wir an, Sie haben auch Folgendes:
Jetzt stoßen wir zur Laufzeit auf Probleme, weil sie
names
etwas enthalten, das kein istinstanceof String
.Vermutlich , wenn Sie wollen
names
nur enthaltenString
, Sie könnten vielleicht noch eine rohe Art verwenden und manuell jeden überprüfenadd
sich selbst, und dann manuell geworfen zuString
jedem Element ausnames
. Noch besser ist es jedoch , KEINEN Rohtyp zu verwenden und den Compiler die ganze Arbeit für Sie erledigen zu lassen , wobei die Leistung von Java-Generika genutzt wird.Natürlich, wenn Sie DO wollen
names
ein erlaubenBoolean
, dann kann man es erklären , wieList<Object> names
, und der obige Code würde kompilieren.Siehe auch
Wie unterscheidet sich ein Rohtyp von der Verwendung
<Object>
als Typparameter?Das Folgende ist ein Zitat aus Effective Java 2nd Edition, Punkt 23: Verwenden Sie keine Rohtypen in neuem Code :
Betrachten Sie zur Veranschaulichung des Punktes die folgende Methode, die a nimmt
List<Object>
und a anfügtnew Object()
.Generika in Java sind unveränderlich. A
List<String>
ist kein aList<Object>
, daher würde Folgendes eine Compiler-Warnung generieren:Wenn Sie deklariert hätten
appendNewObject
, einen RohtypList
als Parameter zu verwenden, würde dies kompiliert, und Sie würden daher die Typensicherheit verlieren, die Sie von Generika erhalten.Siehe auch
<E extends Number>
und<Number>
?Wie unterscheidet sich ein Rohtyp von der Verwendung
<?>
als Typparameter?List<Object>
,List<String>
Usw. sind alleList<?>
, so dass es nur verlockend sein sagen , dass sie gerade sind ,List
statt. Es gibt jedoch einen großen Unterschied: Da nur aList<E>
definiert istadd(E)
, können Sie einem nicht jedes beliebige Objekt hinzufügenList<?>
. Auf der anderen Seite, da der RohtypList
keine Typensicherheit hat, können Sie fastadd
alles zu einemList
.Betrachten Sie die folgende Variation des vorherigen Snippets:
Der Compiler hat Sie wunderbar davor geschützt, die Typinvarianz von
List<?>
! Wenn Sie den Parameter als Rohtyp deklariert habenList list
hätten, würde der Code kompiliert und Sie würden die Typinvariante von verletzenList<String> names
.Ein Rohtyp ist das Löschen dieses Typs
Zurück zu JLS 4.8:
Einfacher ausgedrückt, wenn ein Roh - Typ verwendet wird, die Konstrukteure, Instanzmethoden und nicht
static
sind Felder ebenfalls gelöscht .Nehmen Sie das folgende Beispiel:
Wenn wir das Raw verwenden
MyType
,getNames
wird es ebenfalls gelöscht, so dass es ein Raw zurückgibtList
!JLS 4.6 erklärt weiterhin Folgendes:
Der folgende Fehlerbericht enthält einige Gedanken von Maurizio Cimadamore, einem Compiler-Entwickler, und Alex Buckley, einem der Autoren des JLS, warum dieses Verhalten auftreten sollte: https://bugs.openjdk.java.net/browse / JDK-6400189 . (Kurz gesagt, dies vereinfacht die Spezifikation.)
Wenn es unsicher ist, warum darf es einen Rohtyp verwenden?
Hier ist ein weiteres Zitat aus JLS 4.8:
Effektive Java 2nd Edition hat auch Folgendes hinzuzufügen:
Zusammenfassend sollte der Rohtyp NIEMALS in neuem Code verwendet werden. Sie sollten immer parametrisierte Typen verwenden .
Gibt es keine Ausnahmen?
Da Java-Generika nicht reifiziert sind, gibt es leider zwei Ausnahmen, in denen Rohtypen in neuem Code verwendet werden müssen:
List.class
nichtList<String>.class
instanceof
Operand, zBo instanceof Set
nichto instanceof Set<String>
Siehe auch
Collection<String>.class
illegal?quelle
o instanceof Set<?>
darf die Syntax auch den Rohtyp vermeiden (obwohl dies in diesem Fall nur oberflächlich ist).n
Remote-Beans für jede implementierende Klasse mit identischem Code zu schreiben .TypeName.class
, woTypeName
ein einfacher Bezeichner ( jls ) ist. Hypothetisch gesehen könnte es auch wirklich so sein. Vielleicht als HinweisList<String>.class
ist die Variante, die das JLS speziell als Compilerfehler bezeichnet. Wenn sie ihn also jemals der Sprache hinzufügen, würde ich erwarten, dass dies diejenige ist, die sie verwenden.Rohtypen sind eine alte Geschichte der Java-Sprache. Am Anfang gab es
Collections
und sie hieltenObjects
nicht mehr und nicht weniger. Jede Operation anCollections
erforderlichen Abgüssen vonObject
bis zum gewünschten Typ.Während dies die meiste Zeit funktionierte, traten Fehler auf
Die alten typlosen Sammlungen konnten die Typensicherheit nicht erzwingen, sodass sich der Programmierer daran erinnern musste, was er in einer Sammlung gespeichert hatte.
Generika wurden erfunden, um diese Einschränkung zu umgehen. Der Entwickler deklarierte den gespeicherten Typ einmal und der Compiler tat dies stattdessen.
Zum Vergleich:
Komplexer die vergleichbare Oberfläche:
Beachten Sie, dass es nicht möglich ist, die
CompareAble
Schnittstelle mit zu implementierencompareTo(MyCompareAble)
mit Raw-Typen . Warum sollten Sie sie nicht verwenden:Object
in a gespeichertenCollection
müssen gegossen werden, bevor sie verwendet werden könnenObject
Was der Compiler macht: Generika sind abwärtskompatibel, sie verwenden dieselben Java-Klassen wie die Raw-Typen. Die Magie geschieht meistens zur Kompilierungszeit.
Wird zusammengestellt als:
Dies ist derselbe Code, den Sie schreiben würden, wenn Sie die Rohtypen direkt verwenden würden. Obwohl ich nicht sicher bin, was mit der
CompareAble
Schnittstelle passiert , werden vermutlich zweicompareTo
Funktionen erstellt, von denen eine eineMyCompareAble
und die andere eine übernimmtObject
und sie nach dem Casting an die erste weitergibt.Was sind die Alternativen zu Rohtypen: Verwenden Sie Generika
quelle
Ein Rohtyp ist der Name einer generischen Klasse oder Schnittstelle ohne Typargumente. Zum Beispiel angesichts der generischen Box-Klasse:
Um einen parametrisierten Typ von zu erstellen
Box<T>
, geben Sie ein tatsächliches Typargument für den formalen Typparameter anT
:Wenn das eigentliche Typargument weggelassen wird, erstellen Sie einen Rohtyp von
Box<T>
:Daher
Box
ist der Rohtyp des generischen TypsBox<T>
. Ein nicht generischer Klassen- oder Schnittstellentyp ist jedoch kein Rohtyp.Raw-Typen werden im Legacy-Code angezeigt, da viele API-Klassen (wie die Collections-Klassen) vor JDK 5.0 nicht generisch waren. Wenn Sie Raw-Typen verwenden, erhalten Sie im Wesentlichen ein Verhalten vor Generika - a
Box
gibt IhnenObject
s. Aus Gründen der Abwärtskompatibilität ist es zulässig, seinem Rohtyp einen parametrisierten Typ zuzuweisen:Wenn Sie jedoch einem parametrisierten Typ einen Rohtyp zuweisen, erhalten Sie eine Warnung:
Sie erhalten auch eine Warnung, wenn Sie einen Rohtyp verwenden, um generische Methoden aufzurufen, die im entsprechenden generischen Typ definiert sind:
Die Warnung zeigt, dass Rohtypen generische Typprüfungen umgehen und den Fang von unsicherem Code auf die Laufzeit verschieben. Daher sollten Sie die Verwendung von Rohtypen vermeiden.
Im Abschnitt Typlöschung finden Sie weitere Informationen zur Verwendung von Rohtypen durch den Java-Compiler.
Deaktivierte Fehlermeldungen
Wie bereits erwähnt, können beim Mischen von Legacy-Code mit generischem Code Warnmeldungen auftreten, die den folgenden ähneln:
Dies kann passieren, wenn eine ältere API verwendet wird, die mit Rohtypen arbeitet, wie im folgenden Beispiel gezeigt:
Der Begriff "nicht markiert" bedeutet, dass der Compiler nicht über genügend Typinformationen verfügt, um alle Typprüfungen durchzuführen, die zur Gewährleistung der Typensicherheit erforderlich sind. Die Warnung "nicht markiert" ist standardmäßig deaktiviert, obwohl der Compiler einen Hinweis gibt. Um alle "nicht aktivierten" Warnungen anzuzeigen, kompilieren Sie erneut mit -Xlint: nicht aktiviert.
Wenn Sie das vorherige Beispiel mit -Xlint neu kompilieren: Wenn diese Option nicht aktiviert ist, werden die folgenden zusätzlichen Informationen angezeigt:
Verwenden Sie das Flag -Xlint: -unchecked, um ungeprüfte Warnungen vollständig zu deaktivieren. Die
@SuppressWarnings("unchecked")
Anmerkung unterdrückt ungeprüfte Warnungen. Wenn Sie mit dem nicht vertraut sind@SuppressWarnings
Syntax nicht Anmerkungen.Ursprüngliche Quelle: Java Tutorials
quelle
Ein "roher" Typ in Java ist eine Klasse, die nicht generisch ist und sich eher mit "rohen" Objekten als mit typsicheren generischen Typparametern befasst.
Bevor beispielsweise Java-Generika verfügbar waren, haben Sie eine Auflistungsklasse wie die folgende verwendet:
Wenn Sie Ihr Objekt zur Liste hinzufügen, ist es egal, um welchen Objekttyp es sich handelt, und wenn Sie es aus der Liste erhalten, müssen Sie es explizit in den erwarteten Typ umwandeln.
Mit Generika entfernen Sie den "unbekannten" Faktor, da Sie explizit angeben müssen, welcher Objekttyp in die Liste aufgenommen werden kann:
Beachten Sie, dass Sie bei Generika das aus dem Aufruf get stammende Objekt nicht umwandeln müssen. Die Auflistung ist vordefiniert, um nur mit MyObject zu funktionieren. Diese Tatsache ist der Hauptantriebsfaktor für Generika. Es ändert eine Quelle von Laufzeitfehlern in etwas, das zur Kompilierungszeit überprüft werden kann.
quelle
?
bietet immer noch Typensicherheit. Ich habe es in meiner Antwort behandelt.Sie sollten den Typparameter angeben.
In der Warnung wird darauf hingewiesen, dass Typen unterstützt werden sollen Generika zu parametrisieren, anstatt ihre Rohform zu verwenden.
List
ist definiert, um Generika zu unterstützen :public class List<E>
. Dies ermöglicht viele typsichere Operationen, die zur Kompilierungszeit überprüft werden.quelle
private static List<String> list = new ArrayList<>();
Was ist ein Rohtyp und warum höre ich oft, dass sie nicht in neuem Code verwendet werden sollten?
Ein "Rohtyp" ist die Verwendung einer generischen Klasse, ohne ein oder mehrere Typargumente für ihre parametrisierten Typen anzugeben, z . B.
List
anstelle vonList<String>
. Bei der Einführung von Generika in Java wurden mehrere Klassen aktualisiert, um Generika zu verwenden. Durch die Verwendung dieser Klasse als "Rohtyp" (ohne Angabe eines Typarguments) konnte der Legacy-Code weiterhin kompiliert werden."Raw-Typen" werden aus Gründen der Abwärtskompatibilität verwendet. Ihre Verwendung in neuem Code wird nicht empfohlen, da die Verwendung der generischen Klasse mit einem Typargument eine stärkere Typisierung ermöglicht, was wiederum die Verständlichkeit des Codes verbessern und dazu führen kann, dass potenzielle Probleme früher erkannt werden.
Was ist die Alternative, wenn wir keine Rohtypen verwenden können, und wie ist es besser?
Die bevorzugte Alternative besteht darin, generische Klassen wie beabsichtigt zu verwenden - mit einem geeigneten Typargument (z
List<String>
. ). Auf diese Weise kann der Programmierer Typen genauer angeben, zukünftigen Betreuern mehr Bedeutung für die beabsichtigte Verwendung einer Variablen oder Datenstruktur vermitteln und der Compiler kann eine bessere Typensicherheit erzwingen. Diese Vorteile zusammen können die Codequalität verbessern und dazu beitragen, die Einführung einiger Codierungsfehler zu verhindern.Beispiel: Bei einer Methode, bei der der Programmierer sicherstellen möchte, dass eine List-Variable mit dem Namen "names" nur Strings enthält:
quelle
polygenelubricants
die "Raw Type" -Referenzen von stackoverflow.com/questions/2770111/… in meine eigene Antwort zu kopieren , aber ich nehme an, ich lasse sie für seine / ihre eigene Antwort.Der Compiler möchte, dass Sie Folgendes schreiben:
Andernfalls können Sie einen beliebigen Typ hinzufügen
list
, wodurch die Instanziierungnew ArrayList<String>()
sinnlos wird. Java-Generika sind nur eine Funktion zur Kompilierungszeit, dahernew ArrayList<String>()
akzeptiert ein Objekt, das mit erstellt wurde , ElementeInteger
oderJFrame
Elemente, wenn sie einer Referenz des "Rohtyps" zugewiesen sind.List
Das Objekt selbst weiß nichts darüber, welche Typen es enthalten soll, nur der Compiler.quelle
Hier betrachte ich mehrere Fälle, durch die Sie das Konzept klären können
Fall 1
ArrayList<String> arr
Es ist eineArrayList
Referenzvariable mit Typ,String
die auf einArralyList
Objekt vom Typ verweistString
. Dies bedeutet, dass es nur Objekte vom Typ String enthalten kann.Es ist streng,
String
kein Rohtyp zu sein, daher wird niemals eine Warnung ausgegeben.Fall 2
In diesem Fall
ArrayList<String> arr
handelt es sich um einen strengen Typ, aber Ihr Objektnew ArrayList();
ist ein Rohtyp.Hier
arr
ist ein strenger Typ. Daher wird beim Hinzufügen von a ein Fehler bei der Kompilierungszeit ausgelöstinteger
.Fall 3
In diesem Fall
ArrayList arr
handelt es sich um einen Rohtyp, aber Ihr Objektnew ArrayList<String>();
ist ein strikter Typ.Es wird jeder Objekttyp hinzugefügt, da
arr
es sich um einen Raw-Typ handelt.quelle
Ein Raw- Typ ist das Fehlen eines Typparameters bei Verwendung eines generischen Typs.
Der Raw-Typ sollte nicht verwendet werden, da dies zu Laufzeitfehlern führen kann, z. B. zum Einfügen eines
double
in einenSet
vonint
s.Wenn Sie das Zeug aus dem
Set
abrufen, wissen Sie nicht, was herauskommt. Nehmen wir an, Sie erwarten, dass allesint
s ist, auf das Sie es werfenInteger
. Ausnahme zur Laufzeit, wenn diedouble
3.45 kommt.Wenn Sie einen Typparameter hinzufügen
Set
, wird sofort ein Kompilierungsfehler angezeigt. Mit diesem vorbeugenden Fehler können Sie das Problem beheben, bevor zur Laufzeit etwas in die Luft sprengt (wodurch Zeit und Mühe gespart werden).quelle
Hier ist ein weiterer Fall, in dem rohe Typen Sie beißen:
Wie in der akzeptierten Antwort erwähnt, verlieren Sie jegliche Unterstützung für Generika innerhalb des Codes des Rohtyps. Jeder Typparameter wird in seine Löschung konvertiert (was im obigen Beispiel nur der Fall ist
Object
).quelle
Was ist zu sagen ist , dass Ihr
list
eine istList
von unespecified Objekte. Das heißt, Java weiß nicht, welche Art von Objekten sich in der Liste befinden. Wenn Sie dann die Liste iterieren möchten, müssen Sie jedes Element umwandeln, um auf die Eigenschaften dieses Elements zugreifen zu können (in diesem Fall String).Im Allgemeinen ist es eine bessere Idee, die Sammlungen zu parametrisieren, damit Sie keine Konvertierungsprobleme haben. Sie können nur Elemente des parametrisierten Typs hinzufügen und Ihr Editor bietet Ihnen die geeigneten Methoden zur Auswahl an.
quelle
Tutorial-Seite .
Ein Rohtyp ist der Name einer generischen Klasse oder Schnittstelle ohne Typargumente. Zum Beispiel angesichts der generischen Box-Klasse:
Um einen parametrisierten Box-Typ zu erstellen, geben Sie ein tatsächliches Typargument für den formalen Typparameter T an:
Wenn das eigentliche Typargument weggelassen wird, erstellen Sie einen rohen Typ von Box:
quelle
Vermeiden Sie rohe Typen
Zum Beispiel ,
Eine Liste ist ein Rohtyp, während
List<String>
es sich um einen parametrisierten Typ handelt.Bei der Einführung von Generika in JDK 1.5 wurden Raw-Typen nur beibehalten, um die Abwärtskompatibilität mit älteren Java-Versionen zu gewährleisten. Obwohl die Verwendung von Rohtypen weiterhin möglich ist,
Sie sollten vermieden werden :
Sie sind weniger ausdrucksvoll , und nicht selbst Dokument in der gleichen Weise wie parametrisierte Typen Beispiel
Als Referenz : https://docs.oracle.com/javase/tutorial/java/generics/rawTypes.html
quelle
Ich fand diese Seite, nachdem ich einige Beispielübungen gemacht hatte und genau die gleiche Verwirrung hatte.
============== Ich ging von diesem Code aus, wie im Beispiel angegeben ===============
===================== Zu diesem Code ========================
================================================== =============================
Es mag sicherer sein, aber es hat 4 Stunden gedauert, um die Philosophie zu entwirren ...
quelle
Rohe Typen sind in Ordnung, wenn sie ausdrücken, was Sie ausdrücken möchten.
Beispielsweise kann eine Deserialisierungsfunktion a zurückgeben
List
, kennt jedoch den Elementtyp der Liste nicht. SoList
ist hier der passende Rückgabetyp.quelle