Ich habe heute einen Tweet gelesen , der sagte:
Es ist lustig, wenn sich Java-Benutzer über das Löschen von Typen beschweren, was das einzige ist, was Java richtig gemacht hat, während sie alle Dinge ignorieren, die falsch gemacht wurden.
Meine Frage lautet also:
Gibt es Vorteile von Javas Typlöschung? Welche technischen oder programmtechnischen Vorteile bietet es (möglicherweise) außer der Präferenz für JVM-Implementierungen hinsichtlich Abwärtskompatibilität und Laufzeitleistung?
java
type-erasure
vertti
quelle
quelle
Antworten:
Typ Löschung ist gut
Bleiben wir bei den Fakten
Viele der bisherigen Antworten betreffen den Twitter-Nutzer übermäßig. Es ist hilfreich, sich auf die Nachrichten und nicht auf den Messenger zu konzentrieren. Selbst mit den bisher genannten Auszügen gibt es eine ziemlich konsistente Botschaft:
Ein Ziel: vernünftige Programme
Diese Tweets spiegeln eine Perspektive wider, die nicht daran interessiert ist, ob wir die Maschine dazu bringen können, etwas zu tun , sondern vielmehr, ob wir begründen können, dass die Maschine etwas tut, was wir tatsächlich wollen. Gute Argumentation ist ein Beweis. Beweise können in formaler Notation oder etwas weniger Formalem angegeben werden. Unabhängig von der Spezifikationssprache müssen sie klar und streng sein. Informelle Spezifikationen sind nicht unmöglich korrekt zu strukturieren, weisen jedoch häufig Fehler in der praktischen Programmierung auf. Wir haben am Ende Abhilfemaßnahmen wie automatisierte und explorative Tests, um die Probleme auszugleichen, die wir mit informellen Überlegungen haben. Dies bedeutet nicht, dass das Testen an sich eine schlechte Idee ist, aber der zitierte Twitter-Benutzer schlägt vor, dass es einen viel besseren Weg gibt.
Unser Ziel ist es daher, korrekte Programme zu haben, über die wir klar und konsequent in einer Weise nachdenken können, die der tatsächlichen Ausführung des Programms durch die Maschine entspricht. Dies ist jedoch nicht das einzige Ziel. Wir wollen auch, dass unsere Logik ein gewisses Maß an Ausdruckskraft hat. Zum Beispiel gibt es nur so viel, was wir mit Aussagenlogik ausdrücken können. Es ist schön, eine universelle (∀) und existenzielle (∃) Quantifizierung aus so etwas wie Logik erster Ordnung zu haben.
Verwenden von Typensystemen zum Denken
Diese Ziele können von Typsystemen sehr gut angesprochen werden. Dies ist besonders deutlich aufgrund der Curry-Howard-Korrespondenz . Diese Entsprechung wird oft mit folgender Analogie ausgedrückt: Typen beziehen sich auf Programme wie Theoreme auf Beweise.
Diese Entsprechung ist etwas tiefgreifend. Wir können logische Ausdrücke nehmen und sie durch die Entsprechung zu Typen übersetzen. Wenn wir dann ein Programm mit derselben Typensignatur haben, die kompiliert wird, haben wir bewiesen, dass der logische Ausdruck universell wahr ist (eine Tautologie). Dies liegt daran, dass die Korrespondenz in beide Richtungen erfolgt. Die Transformation zwischen der Typ- / Programm- und der Theorem- / Beweiswelt ist mechanisch und kann in vielen Fällen automatisiert werden.
Curry-Howard spielt gut mit dem, was wir mit Spezifikationen für ein Programm machen möchten.
Sind Typsysteme in Java nützlich?
Selbst mit einem Verständnis von Curry-Howard fällt es manchen Menschen leicht, den Wert eines Typsystems zu verwerfen, wenn dies der Fall ist
In Bezug auf den ersten Punkt machen IDEs das Typensystem von Java möglicherweise einfach genug, um damit zu arbeiten (das ist sehr subjektiv).
In Bezug auf den zweiten Punkt entspricht Java fast einer Logik erster Ordnung. Generika verwenden das Typensystem, das der universellen Quantifizierung entspricht. Leider geben Wildcards nur einen kleinen Teil der existenziellen Quantifizierung. Aber die universelle Quantifizierung ist ein ziemlich guter Anfang. Es ist schön sagen zu können, dass Funktionen
List<A>
für alle möglichen Listen universell funktionieren, da A völlig frei ist. Dies führt zu dem, worüber der Twitter-Nutzer in Bezug auf "Parametrizität" spricht.Ein oft zitiertes Papier über Parametrizität sind Philip Wadlers Theoreme kostenlos! . Das Interessante an diesem Artikel ist, dass wir allein anhand der Typensignatur einige sehr interessante Invarianten nachweisen können. Wenn wir automatisierte Tests für diese Invarianten schreiben würden, würden wir unsere Zeit sehr verschwenden. Zum Beispiel für
List<A>
, von der Typensignatur allein fürflatten
das können wir begründen
Das ist ein einfaches Beispiel, und Sie können wahrscheinlich informell darüber nachdenken, aber es ist noch schöner, wenn wir solche Beweise formell kostenlos vom Typsystem erhalten und vom Compiler überprüft werden.
Nicht löschen kann zu Missbrauch führen
Aus der Perspektive der Sprachimplementierung spielen Javas Generika (die universellen Typen entsprechen) sehr stark mit der Parametrizität, die verwendet wird, um Beweise für die Funktionsweise unserer Programme zu erhalten. Dies führt zu dem dritten erwähnten Problem. All diese Beweise und Korrektheiten erfordern ein fehlerfreies Soundsystem. Java hat definitiv einige Sprachfunktionen, die es uns ermöglichen, unsere Argumentation zu zerstören. Dazu gehören unter anderem:
Nicht gelöschte Generika hängen in vielerlei Hinsicht mit Reflexion zusammen. Ohne Löschung gibt es Laufzeitinformationen, die in der Implementierung enthalten sind, mit der wir unsere Algorithmen entwerfen können. Dies bedeutet, dass wir statisch gesehen nicht das vollständige Bild haben, wenn wir über Programme nachdenken. Reflexion gefährdet ernsthaft die Richtigkeit von Beweisen, über die wir statisch argumentieren. Es ist kein Zufall, dass Reflexion auch zu einer Vielzahl kniffliger Mängel führt.
Wie können nicht gelöschte Generika "nützlich" sein? Betrachten wir die im Tweet erwähnte Verwendung:
Was passiert, wenn T keinen Konstruktor ohne Argumente hat? In einigen Sprachen ist das, was Sie erhalten, null. Oder Sie überspringen den Nullwert und lösen direkt eine Ausnahme aus (zu der Nullwerte ohnehin zu führen scheinen). Da unsere Sprache Turing vollständig ist, ist es unmöglich zu überlegen, welche Aufrufe
broken
"sichere" Typen mit Konstruktoren ohne Argumente beinhalten und welche nicht. Wir haben die Gewissheit verloren, dass unser Programm universell funktioniert.Löschen bedeutet, dass wir überlegt haben (also löschen wir)
Wenn wir also über unsere Programme nachdenken möchten, wird dringend empfohlen, keine Sprachfunktionen zu verwenden, die unsere Argumentation stark gefährden. Wenn wir das getan haben, warum nicht einfach die Typen zur Laufzeit löschen? Sie werden nicht benötigt. Wir können eine gewisse Effizienz und Einfachheit erzielen, wenn wir zufrieden sind, dass keine Casts fehlschlagen oder dass beim Aufrufen möglicherweise Methoden fehlen.
Das Löschen fördert das Denken.
quelle
Typen sind ein Konstrukt, mit dem Programme so geschrieben werden, dass der Compiler die Richtigkeit eines Programms überprüfen kann. Ein Typ ist ein Satz für einen Wert - der Compiler überprüft, ob dieser Satz wahr ist.
Während der Ausführung eines Programms sollten keine Typinformationen erforderlich sein - dies wurde bereits vom Compiler überprüft. Dem Compiler sollte es freigestellt sein, diese Informationen zu verwerfen, um Optimierungen am Code vorzunehmen - ihn schneller laufen zu lassen, eine kleinere Binärdatei zu generieren usw. Das Löschen von Typparametern erleichtert dies.
Java unterbricht die statische Typisierung, indem es ermöglicht, dass Typinformationen zur Laufzeit abgefragt werden - Reflection, Instanz usw. Dies ermöglicht es Ihnen, Programme zu erstellen, die nicht statisch überprüft werden können - sie umgehen das Typsystem. Es fehlen auch Möglichkeiten zur statischen Optimierung.
Die Tatsache, dass Typparameter gelöscht werden, verhindert, dass einige Instanzen dieser falschen Programme erstellt werden. Weitere falsche Programme würden jedoch nicht zugelassen, wenn mehr Typinformationen gelöscht und die Reflexion und Instanz von Einrichtungen entfernt würden.
Das Löschen ist wichtig, um die Eigenschaft der "Parametrizität" eines Datentyps aufrechtzuerhalten. Angenommen, ich habe einen Typ "Liste", der über den Komponententyp T parametrisiert ist, dh Liste <T>. Dieser Typ ist ein Vorschlag, dass dieser Listentyp für jeden Typ T identisch funktioniert. Die Tatsache, dass T ein abstrakter, unbegrenzter Typparameter ist, bedeutet, dass wir nichts über diesen Typ wissen und daher daran gehindert werden, für spezielle Fälle von T etwas Besonderes zu tun.
Beispiel: Ich habe eine Liste xs = asList ("3"). Ich füge ein Element hinzu: xs.add ("q"). Am Ende habe ich ["3", "q"]. Da dies parametrisch ist, kann ich annehmen, dass List xs = asList (7); xs.add (8) endet mit [7,8] Ich weiß aus dem Typ, dass es nicht eine Sache für String und eine Sache für Int macht.
Außerdem weiß ich, dass die List.add-Funktion keine Werte von T aus der Luft erfinden kann. Ich weiß, dass, wenn meiner asList ("3") eine "7" hinzugefügt wird, die einzig möglichen Antworten aus den Werten "3" und "7" konstruiert werden. Es besteht keine Möglichkeit, dass der Liste eine "2" oder "z" hinzugefügt wird, da die Funktion diese nicht erstellen kann. Keiner dieser anderen Werte wäre sinnvoll hinzuzufügen, und die Parametrizität verhindert, dass diese falschen Programme erstellt werden.
Grundsätzlich verhindert das Löschen, dass die Parametrizität verletzt wird, wodurch die Möglichkeit falscher Programme ausgeschlossen wird, was das Ziel der statischen Typisierung ist.
quelle
(Obwohl ich hier bereits eine Antwort geschrieben habe und diese Frage zwei Jahre später erneut aufgegriffen habe, stelle ich fest, dass es eine andere, völlig andere Art der Beantwortung gibt. Daher lasse ich die vorherige Antwort intakt und füge diese hinzu.)
Es ist höchst fraglich, ob der mit Java Generics durchgeführte Prozess den Namen "Typlöschung" verdient. Da generische Typen nicht gelöscht, sondern durch ihre rohen Gegenstücke ersetzt werden, scheint eine bessere Wahl die "Typverstümmelung" zu sein.
Die Quintessenz des Löschens von Typen im allgemein verständlichen Sinne besteht darin, dass die Laufzeit gezwungen wird, innerhalb der Grenzen des statischen Typsystems zu bleiben, indem sie für die Struktur der Daten, auf die zugegriffen wird, "blind" gemacht wird. Dies gibt dem Compiler die volle Leistung und ermöglicht es ihm, Theoreme nur auf der Grundlage statischer Typen zu beweisen. Es hilft dem Programmierer auch, indem es die Freiheitsgrade des Codes einschränkt und dem einfachen Denken mehr Macht verleiht.
Javas Typlöschung erreicht dies nicht - sie lähmt den Compiler wie in diesem Beispiel:
(Die beiden oben genannten Deklarationen werden nach dem Löschen in dieselbe Methodensignatur zusammengefasst.)
Auf der anderen Seite kann die Laufzeit immer noch den Typ eines Objekts und den Grund dafür untersuchen. Da jedoch die Einsicht in den wahren Typ durch Löschen beeinträchtigt wird, sind Verstöße gegen statische Typen trivial zu erreichen und schwer zu verhindern.
Um die Dinge noch komplizierter zu machen, existieren die ursprünglichen und gelöschten Typensignaturen nebeneinander und werden beim Kompilieren parallel berücksichtigt. Dies liegt daran, dass es beim gesamten Prozess nicht darum geht, Typinformationen aus der Laufzeit zu entfernen, sondern darum, ein generisches Typsystem in ein altes Rohtypsystem zu integrieren, um die Abwärtskompatibilität aufrechtzuerhalten. Dieses Juwel ist ein klassisches Beispiel:
(Die Redundanz
extends Object
musste hinzugefügt werden, um die Abwärtskompatibilität der gelöschten Signatur zu gewährleisten.)Lassen Sie uns in diesem Sinne das Zitat noch einmal betrachten:
Was genau hat Java richtig gemacht? Ist es das Wort selbst, unabhängig von der Bedeutung? Schauen Sie sich zum Kontrast den bescheidenen
int
Typ an: Es wird nie eine Laufzeit-Typprüfung durchgeführt oder ist sogar möglich, und die Ausführung ist immer perfekt typsicher. So sieht das Löschen aus, wenn es richtig gemacht wird: Sie wissen nicht einmal, dass es da ist.quelle
Das einzige, was ich hier überhaupt nicht sehe, ist, dass der Laufzeitpolymorphismus von OOP im Wesentlichen von der Reifizierung von Typen zur Laufzeit abhängt. Wenn eine Sprache, deren Rückgrat durch abgelehnte Typen an Ort und Stelle gehalten wird, eine wesentliche Erweiterung ihres Typensystems einführt und es auf der Typlöschung basiert, ist kognitive Dissonanz das unvermeidliche Ergebnis. Genau dies ist der Java-Community passiert. Aus diesem Grund hat das Löschen von Typen so viele Kontroversen ausgelöst, und letztendlich gibt es Pläne, es in einer zukünftigen Version von Java rückgängig zu machen . Etwas Lustiges in dieser Beschwerde von Java-Benutzern zu finden, verrät entweder ein ehrliches Missverständnis des Geistes von Java oder einen bewusst abwertenden Witz.
Die Behauptung "Löschen ist das einzige, was Java richtig gemacht hat" impliziert die Behauptung, dass "alle Sprachen, die auf dynamischem Versand gegen das Funktionsargument vom Laufzeittyp basieren, grundlegend fehlerhaft sind". Obwohl dies sicherlich eine legitime Behauptung für sich ist und sogar als gültige Kritik an allen OOP-Sprachen einschließlich Java angesehen werden kann, kann sie sich nicht als zentraler Punkt für die Bewertung und Kritik von Features im Kontext von Java , wo Laufzeitpolymorphismus auftritt, festlegen ist axiomatisch.
Zusammenfassend kann man sagen, dass "Typlöschung der richtige Weg für das Sprachdesign ist", Positionen, die die Typlöschung in Java unterstützen, einfach deshalb falsch platziert sind, weil es dafür viel, viel zu spät ist und dies bereits im historischen Moment war als Oak von Sun umarmt und in Java umbenannt wurde.
Ob die statische Typisierung selbst die richtige Richtung für die Gestaltung von Programmiersprachen ist, passt in einen viel breiteren philosophischen Kontext dessen, was unserer Meinung nach die Aktivität der Programmierung ausmacht . Eine Denkschule, die eindeutig aus der klassischen Tradition der Mathematik stammt, sieht Programme als Instanzen des einen oder anderen mathematischen Konzepts (Sätze, Funktionen usw.), aber es gibt eine ganz andere Klasse von Ansätzen, die das Programmieren als einen Weg dazu sehen Sprechen Sie mit der Maschine und erklären Sie, was wir von ihr wollen. Aus dieser Sicht ist das Programm eine dynamische, organisch wachsende Einheit, ein dramatisches Gegenteil des sorgfältig errichteten Gebäudes eines statisch typisierten Programms.
Es erscheint natürlich, die dynamischen Sprachen als einen Schritt in diese Richtung zu betrachten: Die Konsistenz des Programms ergibt sich von unten nach oben, ohne a priori- Konstranten, die es von oben nach unten auferlegen würden. Dieses Paradigma kann als ein Schritt zur Modellierung des Prozesses angesehen werden, bei dem wir Menschen durch Entwicklung und Lernen zu dem werden, was wir sind.
quelle
Ein nachfolgender Beitrag desselben Benutzers in derselben Konversation:
(Dies war eine Antwort auf eine Aussage eines anderen Benutzers, nämlich "es scheint in einigen Situationen, dass 'neues T' besser wäre", wobei die Idee ist, dass dies
new T()
aufgrund der Löschung des Typs unmöglich ist. (Dies ist umstritten - selbst wennT
es unter verfügbar wäre Laufzeit, es könnte eine abstrakte Klasse oder Schnittstelle sein, oder es könnte seinVoid
, oder es könnte ein Konstruktor ohne Argumente fehlen, oder sein Konstruktor ohne Argumente könnte privat sein (z. B. weil es eine Singleton-Klasse sein soll) oder seine Der Konstruktor no-arg könnte eine aktivierte Ausnahme angeben, die von der generischen Methode nicht abgefangen oder angegeben wird - aber das war die Voraussetzung. Unabhängig davon ist es wahr, dass Sie ohne Löschung zumindest schreiben könnenT.class.newInstance()
, was diese Probleme behandelt.))Diese Ansicht, dass Typen isomorph zu Aussagen sind, legt nahe, dass der Benutzer einen Hintergrund in der formalen Typentheorie hat. (S) er mag sehr wahrscheinlich keine "dynamischen Typen" oder "Laufzeit-Typen" und würde ein Java ohne Downcasts und
instanceof
und Reflexion und so weiter bevorzugen . (Stellen Sie sich eine Sprache wie Standard ML vor, die ein sehr reichhaltiges (statisches) Typsystem hat und deren dynamische Semantik überhaupt nicht von Typinformationen abhängt.)Es ist übrigens zu bedenken, dass der Benutzer trollt: Während er wahrscheinlich (statisch) typisierte Sprachen aufrichtig bevorzugt, versucht er nicht aufrichtig, andere von dieser Ansicht zu überzeugen. Der Hauptzweck des ursprünglichen Tweets bestand vielmehr darin, diejenigen zu verspotten, die nicht einverstanden sind, und nachdem einige dieser nicht einverstanden waren, veröffentlichte der Benutzer Follow-up-Tweets wie "Der Grund, warum Java Typlöschung hat, ist, dass Wadler et al. Was wissen." Sie tun, im Gegensatz zu Benutzern von Java ". Leider ist es schwierig herauszufinden, was er tatsächlich denkt. Glücklicherweise bedeutet dies wahrscheinlich auch, dass dies nicht sehr wichtig ist. Menschen mit dem aktuellen Tiefe, ihre Ansichten greifen im Allgemeinen nicht zu Trollen, sind ganz dieser Inhalt frei.
quelle
Eine gute Sache ist, dass es nicht notwendig war, die JVM zu ändern, als Generika eingeführt wurden. Java implementiert Generika nur auf Compilerebene.
quelle
Der Grund, warum das Löschen von Typen eine gute Sache ist, ist, dass die Dinge, die es unmöglich macht, schädlich sind. Das Verhindern der Überprüfung von Typargumenten zur Laufzeit erleichtert das Verstehen und Überlegen von Programmen.
Eine Beobachtung , dass ich etwas kontraintuitiv gefunden, dass , wenn Funktionssignaturen sind mehr Generika, werden sie leichter zu verstehen. Dies liegt daran, dass die Anzahl möglicher Implementierungen reduziert wird. Stellen Sie sich eine Methode mit dieser Signatur vor, von der wir irgendwie wissen, dass sie keine Nebenwirkungen hat:
Was sind die möglichen Implementierungen dieser Funktion? Sehr viele. Sie können sehr wenig darüber sagen, was diese Funktion tut. Es könnte die Eingabeliste umkehren. Es könnte sein, dass Ints gepaart, summiert und eine Liste mit der halben Größe zurückgegeben werden. Es gibt viele andere Möglichkeiten, die man sich vorstellen kann. Betrachten Sie nun:
Wie viele Implementierungen dieser Funktion gibt es? Da die Implementierung den Typ der Elemente nicht kennen kann, kann jetzt eine große Anzahl von Implementierungen ausgeschlossen werden: Elemente können nicht kombiniert oder zur Liste hinzugefügt oder herausgefiltert werden, et al. Wir beschränken uns auf Dinge wie: Identität (keine Änderung der Liste), Löschen von Elementen oder Umkehren der Liste. Diese Funktion lässt sich allein anhand ihrer Signatur leichter begründen.
Außer… in Java kann man das Typsystem immer betrügen. Da bei der Implementierung dieser generischen Methode beispielsweise
instanceof
Überprüfungen und / oder Umwandlungen für beliebige Typen verwendet werden können, kann unsere auf der Typensignatur basierende Argumentation leicht unbrauchbar werden. Die Funktion könnte den Typ der Elemente überprüfen und basierend auf dem Ergebnis eine beliebige Anzahl von Dingen ausführen. Wenn diese Laufzeit-Hacks zulässig sind, werden parametrisierte Methodensignaturen für uns viel weniger nützlich.Wenn Java keine Typlöschung hätte ( dh, Typargumente wurden zur Laufzeit geändert ), würde dies einfach mehr argumentationsbeeinträchtigende Spielereien dieser Art ermöglichen. Im obigen Beispiel kann die Implementierung die durch die Typensignatur festgelegten Erwartungen nur verletzen, wenn die Liste mindestens ein Element enthält. Wenn es
T
jedoch geändert wurde, konnte es dies auch dann tun, wenn die Liste leer war. Reifizierte Typen würden nur die (bereits sehr vielen) Möglichkeiten erhöhen, unser Verständnis des Codes zu behindern.Durch das Löschen von Schriftarten wird die Sprache weniger "leistungsfähig". Aber einige Formen von "Macht" sind tatsächlich schädlich.
quelle
instanceof
unsere Fähigkeit beeinträchtigen, darüber nachzudenken, was Code basierend auf Typen tut. Wenn Java Typargumente erneut bestätigen würde, würde dies das Problem nur verschlimmern. Durch das Löschen von Typen zur Laufzeit wird das Typsystem nützlicher.Dies ist keine direkte Antwort (OP fragte "Was sind die Vorteile", ich antworte "Was sind die Nachteile")
Im Vergleich zum System vom Typ C # ist das Löschen vom Typ Java für zwei Raesons ein echtes Problem
Sie können eine Schnittstelle nicht zweimal implementieren
In C # können Sie beide
IEnumerable<T1>
undIEnumerable<T2>
sicher implementieren , insbesondere wenn die beiden Typen keinen gemeinsamen Vorfahren haben (dh ihr Vorfahr istObject
).Praktisches Beispiel: In Spring Framework können Sie nicht
ApplicationListener<? extends ApplicationEvent>
mehrmals implementieren . Wenn Sie unterschiedliche VerhaltensweisenT
benötigen, müssen Sie diese testeninstanceof
Du kannst kein neues T () machen
(und Sie benötigen dazu einen Verweis auf Class)
Wie andere kommentierten, kann das Äquivalent von
new T()
nur durch Reflektion erfolgen, nur durch Aufrufen einer Instanz vonClass<T>
, wobei sichergestellt wird, dass die vom Konstruktor benötigten Parameter eingehalten werden. Mit C # können Sie diesnew T()
nur tun , wenn Sie sichT
auf einen parameterlosen Konstruktor beschränken. WennT
diese Einschränkung nicht eingehalten wird, wird ein Kompilierungsfehler ausgelöst .In Java werden Sie häufig gezwungen sein, Methoden zu schreiben, die wie folgt aussehen
Die Nachteile im obigen Code sind:
ReflectiveOperationException
wird zur Laufzeit ausgelöstWenn ich der Autor von C # wäre, hätte ich die Möglichkeit eingeführt, eine oder mehrere Konstruktoreinschränkungen anzugeben, die zum Zeitpunkt der Kompilierung leicht zu überprüfen sind (daher kann ich beispielsweise einen Konstruktor mit
string,string
Parametern benötigen). Aber der letzte ist Spekulationquelle
Ein zusätzlicher Punkt, den keine der anderen Antworten berücksichtigt zu haben scheint: Wenn Sie wirklich Generika mit Laufzeit-Typisierung benötigen, können Sie diese wie folgt selbst implementieren :
Diese Klasse ist dann in der Lage, alle Dinge zu tun, die standardmäßig erreichbar wären, wenn Java keine Löschung verwenden würde: Sie kann neue
T
s zuweisen (vorausgesetzt, sieT
verfügt über einen Konstruktor, der dem erwarteten Muster entspricht) oder Arrays vonT
s Testen Sie zur Laufzeit dynamisch, ob es sich bei einem bestimmten Objekt um ein Objekt handelt,T
und ändern Sie das Verhalten in Abhängigkeit davon usw.Beispielsweise:
quelle
vermeidet das Aufblähen von c ++ - ähnlichem Code, da derselbe Code für mehrere Typen verwendet wird. Das Löschen von Typen erfordert jedoch einen virtuellen Versand, während der c ++ - Code-Bloat-Ansatz nicht virtuell versendete Generika ausführen kann
quelle
Die meisten Antworten befassen sich mehr mit der Programmierphilosophie als mit den tatsächlichen technischen Details.
Und obwohl diese Frage älter als 5 Jahre ist, bleibt die Frage bestehen: Warum ist eine Typlöschung aus technischer Sicht wünschenswert? Am Ende ist die Antwort ziemlich einfach (auf einer höheren Ebene): https://en.wikipedia.org/wiki/Type_erasure
C ++ - Vorlagen sind zur Laufzeit nicht vorhanden. Der Compiler gibt für jeden Aufruf eine vollständig optimierte Version aus, sodass die Ausführung nicht von Typinformationen abhängt. Aber wie geht eine JIT mit verschiedenen Versionen derselben Funktion um? Wäre es nicht besser, nur eine Funktion zu haben? Ich möchte nicht, dass die JIT alle verschiedenen Versionen davon optimieren muss. Nun, aber was ist dann mit Typensicherheit? Ich denke, das muss aus dem Fenster gehen.
Aber Moment mal: Wie macht .NET das? Reflexion! Auf diese Weise müssen sie nur eine Funktion optimieren und Informationen zum Laufzeittyp abrufen. Und deshalb waren .NET-Generika früher langsamer (obwohl sie viel besser geworden sind). Ich behaupte nicht, dass das nicht bequem ist! Aber es ist teuer und sollte nicht verwendet werden, wenn es nicht unbedingt notwendig ist (es wird in dynamisch typisierten Sprachen nicht als teuer angesehen, da der Compiler / Interpreter sowieso auf Reflexion angewiesen ist).
Auf diese Weise ist die generische Programmierung mit Typlöschung nahezu kein Overhead (einige Laufzeitprüfungen / Umwandlungen sind noch erforderlich): https://docs.oracle.com/javase/tutorial/java/generics/erasure.html
quelle