Bevat de handtekening van een methode het retourtype in Java?

1. Overzicht

De methodehandtekening is slechts een subset van de volledige methodedefinitie in Java. De exacte anatomie van de handtekening kan dus verwarring veroorzaken.

In deze zelfstudie leren we de elementen van de methodehandtekening en de implicaties ervan voor Java-programmering.

2. Methode Handtekening

Methoden in Java ondersteunen overbelasting, wat betekent dat meerdere methoden met dezelfde naam kunnen worden gedefinieerd in dezelfde klasse of hiërarchie van klassen. Daarom moet de compiler de methode waarnaar de clientcode verwijst, statisch kunnen binden. Om deze reden is de methode handtekening identificeert op unieke wijze elke methode.

Volgens Oracle is de methode handtekening bestaat uit de naam en parametertypes. Daarom maken alle andere elementen van de declaratie van de methode, zoals modificatoren, retourtype, parameternamen, uitzonderingenlijst en hoofdtekst geen deel uit van de handtekening.

Laten we de overbelasting van methoden eens nader bekijken en hoe deze zich verhoudt tot methodenhandtekeningen.

3. Overbelastingsfouten

Laten we eens kijken naar de volgende code:

public void print () {System.out.println ("Handtekening is: print ()"); } openbare ongeldige print (int parameter) {System.out.println ("Handtekening is: print (int)"); }

Zoals we kunnen zien, compileert de code omdat de methoden verschillende parametertypelijsten hebben. In feite kan de compiler elke aanroep deterministisch aan de ene of de andere binden.

Laten we nu testen of het legaal is om te overbelasten door de volgende methode toe te voegen:

public int print () {System.out.println ("Handtekening is: print ()"); retourneer 0; }

Als we compileren, krijgen we een "methode is al gedefinieerd in de klas" -fout. Dat bewijst de methode retourtype maakt geen deel uit van de methodehandtekening.

Laten we hetzelfde proberen met modificatoren:

private final void print () {System.out.println ("Handtekening is: print ()"); }

We zien nog steeds dezelfde "methode is al gedefinieerd in de klas" -fout. Daarom is de methode handtekening is niet afhankelijk van modificatoren.

Overbelasting door het wijzigen van gegooide uitzonderingen kan worden getest door toe te voegen:

public void print () gooit IllegalStateException {System.out.println ("Handtekening is: print ()"); gooi nieuwe IllegalStateException (); }

Opnieuw zien we de "methode is al gedefinieerd in de klas" -fout, die de throw-verklaring mag geen deel uitmaken van de handtekening.

Het laatste dat we kunnen testen, is of het wijzigen van de parameternamen overbelasting toelaat. Laten we de volgende methode toevoegen:

openbare ongeldige print (int anotherParameter) {System.out.println ("Handtekening is: print (int)"); }

Zoals verwacht krijgen we dezelfde compilatiefout. Dit betekent dat parameternamen hebben geen invloed op de handtekening van de methode.

3. Generics en Type Erasure

Met generieke parameters, type wissen verandert de effectieve handtekening. In feite kan het een botsing veroorzaken met een andere methode die de bovengrens van het generieke type gebruikt in plaats van het generieke token.

Laten we eens kijken naar de volgende code:

public class OverloadingErrors {public void printElement (T t) {System.out.println ("Handtekening is: printElement (T)"); } public void printElement (Serializable o) {System.out.println ("Handtekening is: printElement (Serializable)"); }}

Hoewel de handtekeningen er anders uitzien, kan de compiler na het wissen van het type niet statisch de juiste methode binden.

We kunnen de compiler zien vervangen T met de bovengrens, Serialiseerbaar, vanwege het wissen van het type. Het botst dus met de methode die expliciet wordt gebruikt Serialiseerbaar.

We zouden hetzelfde resultaat zien met het basistype Voorwerp wanneer het generieke type geen binding heeft.

4. Parameterlijsten en polymorfisme

De handtekening van de methode houdt rekening met de exacte typen. Dat betekent dat we een methode kunnen overbelasten waarvan het parametertype een subklasse of superklasse is.

We moeten echter speciale aandacht besteden aan statische binding zal proberen te matchen met behulp van polymorfisme, auto-boxing en typepromotie.

Laten we de volgende code eens bekijken:

public Number som (Geheel getal term1, Geheel getal term2) {System.out.println ("Geheel getallen optellen"); terugkeer term1 + term2; } public Number sum (Number term1, Number term2) {System.out.println ("Getallen optellen"); terugkeer term1.doubleValue () + term2.doubleValue (); } public Number sum (Object term1, Object term2) {System.out.println ("Objecten toevoegen"); return term1.hashCode () + term2.hashCode (); }

De bovenstaande code is volkomen legaal en zal compileren. Er kan verwarring ontstaan ​​bij het aanroepen van deze methoden, omdat we niet alleen de exacte methodehandtekening moeten weten die we aanroepen, maar ook hoe Java statisch bindt op basis van de werkelijke waarden.

Laten we een paar methodeaanroepen onderzoeken die uiteindelijk aan som (geheel getal, geheel getal):

StaticBinding obj = nieuwe StaticBinding (); obj.sum (Integer.valueOf (2), Integer.valueOf (3)); obj.sum (2, 3); obj.sum (2, 0x1);

Voor de eerste aanroep hebben we de exacte parametertypes Geheel getal, geheel getal. Bij de tweede aanroep zal Java automatisch boxen int naar Geheel getal voor ons. Ten slotte zal Java de bytewaarde transformeren 0x1 naar int door middel van typepromotie en vervolgens automatisch naar Geheel getal.

Evenzo hebben we de volgende aanroepen die binden aan som (getal, getal):

obj.sum (2.0d, 3.0d); obj.sum (Float.valueOf (2), Float.valueOf (3));

Bij het eerste telefoontje hebben we dubbele waarden die automatisch worden ingekaderd Dubbele. En dan, door middel van polymorfisme, Dubbele wedstrijden Aantal. Identiek, Vlotter wedstrijden Aantal voor de tweede oproep.

Laten we kijken naar het feit dat beide Vlotter en Dubbele erven van Aantal en Voorwerp. De standaardbinding is echter to Aantal. Dit komt door het feit dat Java automatisch zal matchen met de dichtstbijzijnde super-types die overeenkomen met een methodehandtekening.

Laten we nu eens kijken naar de volgende methodeaanroep:

obj.sum (2; "Jan");

In dit voorbeeld hebben we een int naar Geheel getal auto-box voor de eerste parameter. Er is echter geen som (geheel getal, tekenreeks) overbelasting voor deze methode naam. Bijgevolg zal Java alle parameter-supertypen doorlopen om van de dichtstbijzijnde ouder naar te casten Voorwerp totdat het een match vindt. In dit geval bindt het zich aan som (Object, Object).

Om de standaard binding te wijzigen, kunnen we expliciete parameter casting gebruiken als volgt:

obj.sum ((Object) 2, (Object) 3); obj.sum ((getal) 2, (getal) 3);

5. Vararg-parameters

Laten we nu eens kijken hoe varargs invloed hebben op de effectieve handtekening van de methode en statische binding.

Hier hebben we een overbelaste methode met varargs:

public Number sum (Object term1, Object term2) {System.out.println ("Objecten toevoegen"); return term1.hashCode () + term2.hashCode (); } public Number sum (Object term1, Object ... term2) {System.out.println ("Variabele argumenten toevoegen:" + term2.length); int resultaat = term1.hashCode (); for (Object o: term2) {resultaat + = o.hashCode (); } resultaat teruggeven; }

Dus wat zijn de effectieve handtekeningen van de methoden? Dat hebben we al gezien som (object, object) is de handtekening voor de eerste. Variabele argumenten zijn in wezen arrays, dus de effectieve handtekening voor de tweede na compilatie is som (Object, Object []).

Een lastige vraag is: hoe kunnen we de methode binding kiezen als we maar twee parameters hebben?

Laten we eens kijken naar de volgende oproepen:

obj.sum (new Object (), new Object ()); obj.sum (new Object (), new Object (), new Object ()); obj.sum (new Object (), new Object [] {new Object ()});

Het is duidelijk dat de eerste oproep wordt gekoppeld aan som (object, object) en de tweede tot som (Object, Object []). Om Java te dwingen de tweede methode met twee objecten aan te roepen, moeten we deze in een array plaatsen zoals in de derde aanroep.

Het laatste dat hier moet worden opgemerkt, is dat het declareren van de volgende methode botst met de vararg-versie:

public Number som (Object term1, Object [] term2) {// ...}

6. Conclusie

In deze zelfstudie hebben we geleerd dat de handtekeningen van de methode bestaan ​​uit de lijst met namen en parametertypen. De modificatoren, het retourtype, de parameternamen en de uitzonderingslijst kunnen geen onderscheid maken tussen overbelaste methoden en maken dus geen deel uit van de handtekening.

We hebben ook gekeken naar hoe type erasure en varargs de effectieve methodehandtekening verbergen en hoe we Java's statische methodebinding kunnen negeren.

Zoals gewoonlijk zijn alle codevoorbeelden die in dit artikel worden getoond, beschikbaar op GitHub.