Typ Erasure in Java Explained

1. Overzicht

In dit korte artikel bespreken we de basis van een belangrijk mechanisme in Java's generieke geneesmiddelen, bekend als het wissen van typen.

2. Wat is typeverwijdering?

Het wissen van typen kan worden uitgelegd als het proces van het afdwingen van typebeperkingen alleen tijdens het compileren en het weggooien van de elementtype-informatie tijdens runtime.

Bijvoorbeeld:

openbare statische boolean containsElement (E [] elementen, E element) {for (E e: elementen) {if (e.equals (element)) {retourneer waar; }} return false; }

De compiler vervangt het ongebonden type E. met een echt type Voorwerp:

openbare statische boolean containsElement (Object [] elementen, Object element) {for (Object e: elementen) {if (e.equals (element)) {return true; }} return false; }

Daarom zorgt de compiler voor typeveiligheid van onze code en voorkomt runtime-fouten.

3. Soorten typen wissen

Het wissen van typen kan plaatsvinden op klasse- (of variabele) en methodeniveau.

3.1. Klassetype wissen

Op klassenniveau verwijdert de compiler de typeparameters van de klasse en vervangt deze door de eerste binding, of Voorwerp als de type parameter niet gebonden is.

Laten we een Stapel met behulp van een array:

openbare klasse Stack {privé E [] stackContent; openbare Stack (int capaciteit) {this.stackContent = (E []) nieuw object [capaciteit]; } public void push (E data) {// ..} public E pop () {// ..}}

Bij het compileren vervangt de compiler de parameter niet-gebonden type E. met Voorwerp:

openbare klasse Stack {privé-object [] stackContent; public Stack (int capaciteit) {this.stackContent = (Object []) nieuw object [capaciteit]; } public void push (Objectgegevens) {// ..} public Object pop () {// ..}}

In een geval waarin de type parameter E. is gebonden:

openbare klasse BoundStack {privé E [] stackContent; openbare BoundStack (int capaciteit) {this.stackContent = (E []) nieuw object [capaciteit]; } public void push (E data) {// ..} public E pop () {// ..}}

De compiler zal de parameter gebonden type vervangen E. met de eerste gebonden klas, Vergelijkbaar in dit geval:

openbare klasse BoundStack {privé Comparable [] stackContent; openbare BoundStack (int capaciteit) {this.stackContent = (Vergelijkbaar []) nieuw object [capaciteit]; } public void push (vergelijkbare gegevens) {// ..} public Comparable pop () {// ..}}

3.2. Methode Type wissen

Voor het wissen van het type methode wordt de parameter type van de methode niet opgeslagen maar geconverteerd naar het bovenliggende type Voorwerp als het ongebonden is of als het eerste gebonden klasse is als het gebonden is.

Laten we eens kijken naar een methode om de inhoud van een gegeven array weer te geven:

openbare statische leegte printArray (E [] array) {for (E element: array) {System.out.printf ("% s", element); }}

Bij compilatie vervangt de compiler de parameter type E. met Voorwerp:

openbare statische leegte printArray (Object [] array) {for (Object element: array) {System.out.printf ("% s", element); }}

Voor een parameter van het type gebonden methode:

openbare statische  void printArray (E [] array) {for (E element: array) {System.out.printf ("% s", element); }}

We hebben de parameter type E. gewist en vervangen door Vergelijkbaar:

public static void printArray (Comparable [] array) {for (Comparable element: array) {System.out.printf ("% s", element); }}

4. Randgevallen

Ergens tijdens het wisproces van het type, maakt de compiler een synthetische methode om vergelijkbare methoden te onderscheiden. Deze kunnen afkomstig zijn van methodehandtekeningen die dezelfde eerste gebonden klasse uitbreiden.

Laten we een nieuwe klasse maken die onze vorige implementatie van Stapel. Let op: dit verwijst naar de Stapel klasse waarin we hebben gemaakt paragraaf 3.1, en niet java.util.Stack.

public class IntegerStack breidt Stack uit {public IntegerStack (int capaciteit) {super (capaciteit); } public void push (integer waarde) {super.push (waarde); }}

Laten we nu eens kijken naar de volgende code:

IntegerStack integerStack = nieuwe IntegerStack (5); Stack stack = integerStack; stack.push ("Hallo"); Geheel getal data = integerStack.pop ();

Na het wissen van het type hebben we:

IntegerStack integerStack = nieuwe IntegerStack (5); Stack stack = (IntegerStack) integerStack; stack.push ("Hallo"); Geheel getal data = (String) integerStack.pop ();

Merk op hoe we een String op de IntegerStack - omdat IntegerStack geërfd push (Object) van de bovenliggende klasse Stapel. Dit is natuurlijk niet correct - aangezien het sindsdien een geheel getal zou moeten zijn integerStack is een Stapel type.

Dus, niet verrassend, een poging om knal een Draad en toewijzen aan een Geheel getal veroorzaakt een ClassCastException van een cast die is ingevoegd tijdens de Duwen door de compiler.

4.1. Bridge-methoden

Om het bovenstaande randgeval op te lossen, maakt de compiler soms een bridge-methode. Dit is een synthetische methode die is gemaakt door de Java-compiler tijdens het compileren van een klasse of interface die een geparametriseerde klasse uitbreidt of een geparametriseerde interface implementeert waarbij de handtekeningen van de methode enigszins verschillen of dubbelzinnig kunnen zijn.

In ons voorbeeld hierboven bewaart de Java-compiler het polymorfisme van generieke typen na het wissen door ervoor te zorgen dat de handtekening van de methode niet niet overeenkomt tussen IntegerStack‘S push (geheel getal) methode en Stapel‘S push (Object) methode.

Daarom maakt de compiler hier een bridge-methode:

public class IntegerStack breidt Stack uit {// Bridge-methode gegenereerd door de compiler public void push (Object value) {push ((Integer) value); } public void push (integer waarde) {super.push (waarde); }}

Bijgevolg, Stapel klas Duwen methode na het wissen van het type, delegeert naar het origineel Duwen methode van IntegerStack klasse.

5. Conclusie

In deze zelfstudie hebben we het concept van het wissen van typen besproken met voorbeelden in variabelen en methoden voor typeparameters.

U kunt meer lezen over deze concepten:

  • Java-taalspecificatie: Type Erasure
  • De basisprincipes van Java Generics

Zoals altijd is de broncode die bij dit artikel hoort, beschikbaar op GitHub.