Fragen:
- Was sind Raw-Typen in Java und warum höre ich oft, dass sie nicht in neuem Code verwendet werden sollten?
- Was ist die Alternative, wenn wir keine Rohtypen verwenden können, und wie ist es besser?
Antworten:
Die Java-Sprachspezifikation definiert einen Rohtyp wie folgt:
Ein Rohtyp ist definiert als einer von:
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-
static
Mitgliedstyp eines RohtypsR
, der nicht von einer Oberklasse oder Superschnittstelle von geerbt wirdR
.
Hier ist ein Beispiel zur Veranschaulichung:
public class MyType<E> {
class Inner { }
static class Nested { }
public static void main(String[] args) {
MyType mt; // warning: MyType is a raw type
MyType.Inner inn; // warning: MyType.Inner is a raw type
MyType.Nested nest; // no warning: not parameterized type
MyType<Object> mt1; // no warning: type parameter given
MyType<?> 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 MyType
kurz zu bezeichnen, aber technisch ist der Name MyType<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 handelt MyType<E>
, weil dies der Fall ist static
.
mt1
und mt2
werden beide mit tatsächlichen Typparametern deklariert, sodass es sich nicht um Rohtypen handelt.
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 = new ArrayList(); // 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 names
etwas enthalten, das kein ist instanceof String
.
Vermutlich , wenn Sie wollen names
nur enthalten String
, Sie könnten vielleicht noch eine rohe Art verwenden und manuell jeden überprüfen add
sich selbst, und dann manuell geworfen zu String
jedem 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.
List<String> names = new ArrayList<String>();
names.add("John");
names.add("Mary");
names.add(Boolean.FALSE); // compilation error!
Natürlich, wenn Sie DO wollen names
ein erlauben Boolean
, dann kann man es erklären , wieList<Object> names
, und der obige Code würde kompilieren.
<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
List
und dem parametrisierten TypList<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 aList<String>
an einen Parameter vom Typ übergeben könnenList
, können Sie es nicht an einen Parameter vom Typ übergebenList<Object>
. Es gibt Subtypisierungsregeln für Generika und esList<String>
handelt sich um einen Subtyp des RohtypsList
, jedoch nicht des parametrisierten TypsList<Object>
. Infolgedessen verlieren Sie die Typensicherheit, wenn Sie einen rohen Typ wie verwendenList
, 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()
.
void appendNewObject(List<Object> list) {
list.add(new Object());
}
Generika in Java sind unveränderlich. A List<String>
ist kein a List<Object>
, daher würde Folgendes eine Compiler-Warnung generieren:
List<String> names = new ArrayList<String>();
appendNewObject(names); // compilation error!
Wenn Sie deklariert hätten appendNewObject
, einen Rohtyp List
als Parameter zu verwenden, würde dies kompiliert, und Sie würden daher die Typensicherheit verlieren, die Sie von Generika erhalten.
<?>
als Typparameter?List<Object>
, List<String>
Usw. sind alle List<?>
, so dass es nur verlockend sein sagen , dass sie gerade sind , List
statt. 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 List
keine Typensicherheit hat, können Sie fast add
alles zu einem List
.
Betrachten Sie die folgende Variation des vorherigen Snippets:
static void appendNewObject(List<?> list) {
list.add(new Object()); // compilation error!
}
//...
List<String> names = new ArrayList<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
.
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
static
eines RohtypsC
, der nicht von seinen Oberklassen oder Superschnittstellen geerbt wird, ist der Rohtyp, der dem Löschen seines Typs in der entsprechenden generischen Deklaration entsprichtC
.
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:
class MyType<E> {
List<String> getNames() {
return Arrays.asList("John", "Mary");
}
public static void main(String[] args) {
MyType rawType = new MyType();
// unchecked warning!
// required: List<String> found: List
List<String> names = rawType.getNames();
// compilation error!
// incompatible types: Object cannot be converted to String
for (String str : rawType.getNames())
System.out.print(str);
}
}
Wenn wir das Raw verwenden MyType
, getNames
wird es ebenfalls gelöscht, so dass es ein Raw zurückgibt List
!
JLS 4.6 erklärt weiterhin Folgendes:
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
s
ist eine Signatur, die aus demselben Namen wies
und den Löschungen aller in angegebenen formalen Parametertypen bestehts
.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.)
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 .
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, zB o instanceof Set
nichto instanceof Set<String>
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
, wo TypeName
ein einfacher Bezeichner ( jls ) ist. Hypothetisch gesehen könnte es auch wirklich so sein. Vielleicht als Hinweis List<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.
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 Collections
und sie hielten Objects
nicht mehr und nicht weniger. Jede Operation an Collections
erforderlichen Abgüssen von Object
bis zum gewünschten Typ.
List aList = new ArrayList();
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 = new ArrayList();
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 = new ArrayList<String>();
aNumberList.add("one");
Integer iOne = aNumberList.get(0);//Compile time error
String sOne = aNumberList.get(0);//works fine
Zum Vergleich:
// Old style collections now known as raw types
List aList = new ArrayList(); //Could contain anything
// New style collections with Generics
List<String> aList = new ArrayList<String>(); //Contains only Strings
Komplexer die vergleichbare Oberfläche:
//raw, not type save can compare with Other classes
class MyCompareAble implements CompareAble
{
int id;
public int compareTo(Object other)
{return this.id - ((MyCompareAble)other).id;}
}
//Generic
class MyCompareAble implements CompareAble<MyCompareAble>
{
int id;
public int compareTo(MyCompareAble other)
{return this.id - other.id;}
}
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 gespeicherten Collection
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.
List<String> someStrings = new ArrayList<String>();
someStrings.add("one");
String one = someStrings.get(0);
Wird zusammengestellt als:
List someStrings = new ArrayList();
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 CompareAble
Schnittstelle passiert , werden vermutlich zwei compareTo
Funktionen erstellt, von denen eine eine MyCompareAble
und die andere eine übernimmt Object
und sie nach dem Casting an die erste weitergibt.
Was sind die Alternativen zu Rohtypen: Verwenden Sie Generika
Ein Rohtyp ist der Name einer generischen Klasse oder Schnittstelle ohne Typargumente. Zum Beispiel angesichts der generischen Box-Klasse:
public class Box<T> {
public void set(T t) { /* ... */ }
// ...
}
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 = new Box<>();
Wenn das eigentliche Typargument weggelassen wird, erstellen Sie einen Rohtyp von Box<T>
:
Box rawBox = new Box();
Daher Box
ist 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 Box
gibt Ihnen Object
s. Aus Gründen der Abwärtskompatibilität ist es zulässig, seinem Rohtyp einen parametrisierten Typ zuzuweisen:
Box<String> stringBox = new Box<>();
Box rawBox = stringBox; // OK
Wenn Sie jedoch einem parametrisierten Typ einen Rohtyp zuweisen, erhalten Sie eine Warnung:
Box rawBox = new Box(); // 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:
Box<String> stringBox = new Box<>();
Box rawBox = stringBox;
rawBox.set(8); // warning: unchecked invocation to set(T)
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.
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:
public class WarningDemo {
public static void main(String[] args){
Box<Integer> bi;
bi = createBox();
}
static Box createBox(){
return new Box();
}
}
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.
Ursprüngliche Quelle: Java Tutorials
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 = new LinkedList();
list.add(new MyObject());
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 = new LinkedList<MyObject>();
list.add(new MyObject());
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.
?
bietet immer noch Typensicherheit. Ich habe es in meiner Antwort behandelt.
private static List<String> list = new ArrayList<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.
List
ist definiert, um Generika zu unterstützen : public class List<E>
. Dies ermöglicht viele typsichere Operationen, die zur Kompilierungszeit überprüft werden.
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 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 = new ArrayList<String>();
names.add("John"); // OK
names.add(new Integer(1)); // compile error
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:
private static List<String> list = new ArrayList<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 Integer
oder JFrame
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.
Hier betrachte ich mehrere Fälle, durch die Sie das Konzept klären können
1. ArrayList<String> arr = new ArrayList<String>();
2. ArrayList<String> arr = new ArrayList();
3. ArrayList arr = new ArrayList<String>();
ArrayList<String> arr
Es ist eine ArrayList
Referenzvariable mit Typ, String
die auf ein ArralyList
Objekt vom Typ verweist String
. 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.
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)
In diesem Fall ArrayList<String> arr
handelt 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 arr
ist ein strenger Typ. Daher wird beim Hinzufügen von a ein Fehler bei der Kompilierungszeit ausgelöst integer
.
Warnung : - Ein
Raw
Typobjekt wird auf eineStrict
Typreferenzvariable von verwiesenArrayList
.
In diesem Fall ArrayList arr
handelt 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 arr
es sich um einen Raw-Typ handelt.
Warnung : - Ein
Strict
Typobjekt wird auf eineraw
typbezogene 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 double
in einen Set
von int
s.
Set set = new HashSet();
set.add(3.45); //ok
Wenn Sie das Zeug aus dem Set
abrufen, wissen Sie nicht, was herauskommt. Nehmen wir an, Sie erwarten, dass alles int
s ist, auf das Sie es werfen Integer
. Ausnahme zur Laufzeit, wenn die double
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).
Set<Integer> set = new HashSet<Integer>();
set.add(3.45); //NOT ok.
Hier ist ein weiterer Fall, in dem rohe Typen Sie beißen:
public class StrangeClass<T> {
@SuppressWarnings("unchecked")
public <X> X getSomethingElse() {
return (X)"Testing something else!";
}
public static void main(String[] args) {
final StrangeClass<String> withGeneric = new StrangeClass<>();
final StrangeClass withoutGeneric = new StrangeClass();
final String value1,
value2;
// Compiles
value1 = withGeneric.getSomethingElse();
// Produces compile error:
// incompatible types: java.lang.Object cannot be converted to java.lang.String
value2 = withoutGeneric.getSomethingElse();
}
}
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 list
eine ist List
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.
private static List<String> list = new ArrayList<String>();
Ein Rohtyp ist der Name einer generischen Klasse oder Schnittstelle ohne Typargumente. Zum Beispiel angesichts der generischen Box-Klasse:
public class Box<T> {
public void set(T t) { /* ... */ }
// ...
}
Um einen parametrisierten Box-Typ zu erstellen, geben Sie ein tatsächliches Typargument für den formalen Typparameter T an:
Box<Integer> intBox = new Box<>();
Wenn das eigentliche Typargument weggelassen wird, erstellen Sie einen rohen Typ von Box:
Box rawBox = new Box();
Vermeiden Sie rohe Typen
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 sind weniger ausdrucksvoll , und nicht selbst Dokument in der gleichen Weise wie parametrisierte Typen Beispiel
import java.util.*;
public final class AvoidRawTypes {
void withRawType() {
//Raw List doesn't self-document,
//doesn't state explicitly what it can contain
List 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);
}
}
private void log(Object message) {
System.out.println(Objects.toString(message));
}
}
Als Referenz : https://docs.oracle.com/javase/tutorial/java/generics/rawTypes.html
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 ===============
public static void main(String[] args) throws IOException {
Map wordMap = new HashMap();
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 ========================
public static void main(String[] args) throws IOException {
// replace with TreeMap to get them sorted by name
Map<String, Integer> wordMap = new HashMap<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());
}
}
================================================== =============================
Es mag sicherer sein, aber es hat 4 Stunden gedauert, um die Philosophie zu entwirren ...
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 List
ist hier der passende Rückgabetyp.