Frage

Dies ist keine Frage, was Boxen und Unboxing, es ist eher Warum tut Sprachen wie Java und C #, dass brauchen?

Ich bin sehr vertraut Wtih C ++, STL und kurbeln.

In C ++ ich so etwas wie dies sehr leicht schreiben könnte,

std::vector<double> dummy;

Ich habe einige Erfahrung mit Java, aber ich war wirklich überrascht, weil ich so etwas zu schreiben hatte,

ArrayList<Double> dummy = new ArrayList<Double>();

Meine Frage, warum sollte es sich um ein Objekt sein, was technisch so schwierig ist, primitive Typen umfassen, wenn man über Generics?

War es hilfreich?

Lösung

  

, was technisch so schwierig ist, primitive Typen umfassen, wenn man über Generics reden?

In Java Fall ist es wegen der Art und Weise Generika Arbeit. In Java Generika ist ein Kompilierung-Trick, dass Sie davon ab, ein Image Objekt in eine ArrayList<String> verhindert. Allerdings sind die Java-Generika mit Typ Löschung implementiert: die generischen Typinformationen während der Laufzeit verloren geht. Dies war aus Kompatibilitätsgründen, weil Generika in Java das Leben ziemlich spät hinzugefügt wurden. Dies bedeutet, dass die Laufzeit, ein ArrayList<String> ist effektiv ein ArrayList<Object> (oder besser: gerade ArrayList, die erwartet und kehrt Object in all seinen Methoden), die automatisch auf String wirft, wenn Sie einen Wert abrufen.

Aber da int leitet sich nicht von Object, können Sie es nicht in einem Arraylist setzen, die (zur Laufzeit) Object erwartet und Sie können eine Object int entweder nicht gegossen. Das bedeutet, dass der primitive int muss in eine Art eingewickelt wird, die von Object, wie Integer nicht erben.

C # zum Beispiel funktioniert anders. Generics in C # ist auch zur Laufzeit erzwungen und nicht Box mit einem List<int> erforderlich. Boxen in C # geschieht nur, wenn Sie versuchen, einen Werttyp wie int in einem Referenztypvariable wie object zu speichern. Da int in C # von Object in C # erbt, object obj = 2 Schreiben ist vollkommen gültig, jedoch wird die int eingerahmt werden, der vom Compiler automatisch erfolgt (kein Integer Referenztyp für den Benutzer oder etwas ausgesetzt ist).

Andere Tipps

Boxen und Unboxing sind eine Notwendigkeit, aus dem Weg geboren, die Sprachen (wie C # und Java), um ihre Speicherzuordnungsstrategien umzusetzen.

Bestimmte Typen werden auf dem Stapel und andere auf dem Heap zugeordnet. Um einen Stapel zugewiesene Typ als Heap zugewiesen Art zu behandeln, Boxen benötigt, um den Stapel zugewiesene Typ auf dem Heap zu bewegen. Unboxing ist die Umkehrverfahren.

In C # Stapel zugewiesenen Typen genannt werden Werttypen (zB System.Int32 und System.DateTime) und Heap zugewiesenen Typen werden als Referenztypen (zB System.Stream und System.String).

In einigen Fällen ist es vorteilhaft, in der Lage sein einen Werttyp wie ein Referenz-Typ zu behandeln (Reflexion ist ein Beispiel), aber in den meisten Fällen, Boxen und Unboxing am besten vermieden werden.

Ich glaube, das ist auch weil Primitiven erben nicht von Object. Angenommen, Sie haben eine Methode, die etwas zu akzeptieren, überhaupt als Parameter in der Lage sein möchte, zum Beispiel.

class Printer {
    public void print(Object o) {
        ...
    }
}

Sie müssen möglicherweise einen einfachen Grundwert zu dieser Methode zu übergeben, wie:

printer.print(5);

würden Sie in der Lage sein, dass ohne Boxen / Unboxing zu tun, denn 5 eine primitive und ist kein Objekt. Sie könnten die Druckmethode für jeden primitiven Typen überlasten solche Funktionalität zu aktivieren, aber es ist ein Schmerz.

Ich kann Ihnen nur sagen, für Java, warum es nicht primitve Typen in Generika unterstützt.

Zuerst gab es das Problem, dass die Frage dieses jedes Mal auf die Diskussion gebracht zu unterstützen, wenn Java sogar primitive Typen haben sollte. Was natürlich behindern die Diskussion über die eigentliche Frage.

Zweiter der Hauptgrund, nicht schließen war, dass sie sich auf eine nicht modifizierte binäre Rückwärtskompatibilität, so dass es laufen würde VM nicht bewusst Generika wollte. Diese Abwärtskompatibilität / Migration Kompatibilität Grund ist auch, warum jetzt die Kollektionen API Generika unterstützt und gleich geblieben und es ist nicht (wie in C #, wenn sie Generika eingeführt) eine komplett neue Reihe von einer generischen bewussten Sammlung API.

Die Kompatibilität getan wurde ersure (bei der Kompilierung entfernt generischen Typparameter info) verwendet, die auch der Grund, warum Sie Warnungen in Java bekommen so viele ungeprüft gegossen wird.

Sie könnten noch verdinglichten Generika hinzufügen, aber es ist nicht so einfach. Nur das Hinzufügen der Typ Info Laufzeit hinzufügen anstatt sie zu entfernen, es wird nicht funktionieren, da es Quelle und binäre Kompatibilität bricht (Sie kann nicht fortgesetzt werden rohe Typen verwenden, und Sie können nicht nennen vorhandenen Code kompiliert, weil sie nicht die entsprechenden Methoden haben ).

Der andere Ansatz ist die eine C # chose: siehe oben

Und automatisiert Autoboxing / Unboxing wurde nicht für diesen Anwendungsfall unterstützt, da die Kosten zu viel Autoboxing.

Java Theorie und Praxis: Generics gotchas

In Java und C # (im Gegensatz zu C ++) alles Objekt erstreckt, so Collection-Klassen wie Arraylist kann Objekt oder bei einem seiner Nachkommen halten (im Grunde nichts).

Aus Performance-Gründen jedoch Primitiven in Java oder Werttypen in C #, wurden einen speziellen Status. Sie werden nicht widersprechen. Sie können nicht so etwas wie (in Java) tun:

 7.toString()

Auch wenn toString ist eine Methode auf Objekt. Um diese Anspielung auf die Leistung zu überbrücken, wurden gleichwertige Objekte erstellt. Autoboxing entfernt den vorformulierten Code mit einem primitiven in seiner Wrapper-Klasse setzen und wieder herausnehmen, so dass der Code besser lesbar.

Der Unterschied zwischen Werttypen und Objekten in C # ist mehr grau. Siehe hier darüber, wie sie sind anders.

Jeder Nicht-Array Nicht-String-Objekt auf dem Heap gespeichert enthält ein 8- oder 16-Byte-Header (Größen für 32/64-Bit-Systeme), gefolgt von dem Inhalt dieses Objekts öffentlichen und privaten Bereichen. Arrays und Zeichenfolgen die obige Header plus einige mehr Bytes, die die Länge der Anordnung und Größe jedes Elements (und möglicherweise die Anzahl der Dimensionen, Länge jeder Dimension, etc.) definiert, indem alle Felder des ersten gefolgt Element, dann werden alle Felder des zweiten usw. ein Verweis auf ein Objekt gegeben, kann das System einfach den Header untersuchen und bestimmen, welche Art es ist.

Referenz-Typ-Speicherstellen halten einen Vier- oder Acht-Byte-Wert, der eindeutig ein Objekt auf dem Heap gespeichert identifiziert. In der gegenwärtigen Implementierungen ist dieser Wert ein Zeiger, aber es ist einfacher (und semantisch äquivalent) davon als „Objekt-ID“ zu denken.

Werttyp Speicherstellen halten den Inhalt der Felder des Werttypen, aber haben nicht mit einem Header. Wenn Code eine Variable vom Typ Int32 erklärt, gibt es keine Notwendigkeit, müssen Informationen speichern, damit Int32 zu sagen, was es ist. Die Tatsache, dass dieser Ort eine Int32 hält effektiv als Teil des Programms gespeichert, und damit es nicht selbst in der Lage gespeichert werden muß. Dies ist eine stellen eine große Einsparungen, wenn beispielsweise eine Million Objekte hat, von denen jeder ein Feld vom Typ Int32 haben. Jedes der Objekte die Int32 halten hat einen Header, der die Klasse identifiziert, die es bedienen kann. Da eine Kopie dieser Klasse Code auf einem der Millionen Fällen arbeiten kann, die Tatsache mit, dass das Feld ein Int32 ist Teil des Codes sein ist viel effizienter, als wenn man den Speicher für jedes dieser Felder enthalten Informationen über das, was es ist .

Boxen sind notwendig, wenn ein Antrag gestellt wird, den Inhalt eines Wert-Typ Lagerort Code zu übergeben, die nicht wissen, dass bestimmten Werttyp zu erwarten. Code der Objekte von unbekanntem Typ erwartet auf dem Heap gespeichert, um eine Referenz auf ein Objekt annehmen können. Da jedes Objekt auf dem Heap gespeichert einen Header zu identifizieren, welche Art von Objekt es ist, kann Code, den Header verwenden, wann immer es notwendig ist, ein Objekt in einer Art und Weise zu verwenden, die seine Art zu wissen, erfordern würde.

Beachten Sie, dass in .net, es möglich ist, zu erklären, was generische Klassen und Methoden aufgerufen werden. Jede derartige Erklärung generiert automatisch eine Familie von Klassen oder Methoden, die außer Objekttyp fort er identisch sind, auf denen sie zu handeln erwarten. Wenn man eine Int32 zu einer Routine DoSomething<T>(T param) gibt, wird diese automatisch eine Version der Routine, in der Erzeugung jede Instanz vom Typ T effektiv mit Int32 ersetzt wird. Diese Version der Routine wird wissen, dass jeder Speicherplatz deklarierte als Typ T eine Int32 hält, so wie in dem Fall, in dem eine Routine hartcodiert wurde eine Int32 Lagerort zu verwenden, wird es nicht mit denen, Speichertyp Informationen notwendig sein, Orte selbst.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top