Was ist ein Rohtyp und warum sollten wir ihn nicht verwenden?

662

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?
Polygenschmierstoffe
quelle
Die Java-Tutorials verwenden weiterhin die JComboBox, die diese Warnung verursacht. Welche Version der Combobox verursacht diese Warnung nicht? docs.oracle.com/javase/tutorial/uiswing/components/…
SuperStar
1
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:

JLS 4.8 Rohtypen

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- staticMitgliedstyp eines Rohtyps R, der nicht von einer Oberklasse oder Superschnittstelle von geerbt wird R.

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 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 = 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 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üfen add 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.

List<String> names = new ArrayList<String>();
names.add("John");
names.add("Mary");
names.add(Boolean.FALSE); // compilation error!

Natürlich, wenn Sie DO wollen namesein erlauben Boolean, 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 :

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

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 Listals Parameter zu verwenden, würde dies kompiliert, und Sie würden daher die Typensicherheit verlieren, die Sie von Generika erhalten.

Siehe auch


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:

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.


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 .

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, getNameswird 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 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:

  • Klassenliterale, zB List.classnichtList<String>.class
  • instanceofOperand, zB o instanceof Setnichto instanceof Set<String>

Siehe auch

Polygenschmierstoffe
quelle
16
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 = 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 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 = 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 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

josefx
quelle
30

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 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 = 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.

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:

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

Adelin
quelle
21

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.

Andy White
quelle
3
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
 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.

Listist definiert, um Generika zu unterstützen : public class List<E>. Dies ermöglicht viele typsichere Operationen, die zur Kompilierungszeit überprüft werden.

Bozho
quelle
3
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 = new ArrayList<String>();
names.add("John");          // OK
names.add(new Integer(1));  // compile error
Bert F.
quelle
1
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:

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 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.

Michael Borgwardt
quelle
12

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

Fall 1

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.

Vikrant Kashyap
quelle
8

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 = new HashSet();
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 = new HashSet<Integer>();
set.add(3.45); //NOT ok.
Lars Andren
quelle
7

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

GuyPaddock
quelle
5

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.

private static List<String> list = new ArrayList<String>();
pakore
quelle
4

Tutorial-Seite .

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();
Mykhaylo Adamovych
quelle
2

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 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.*;
    
    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

Ashish
quelle
1

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 ...

user2442615
quelle
0

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.

Stefan Reich
quelle
Sie können verwenden? als Typparameter
Dániel Kis
Ja, aber das ist mehr zu tippen und ich bin dagegen, mehr zu tippen. :)
Stefan Reich