Java >> Java tutorial >  >> Tag >> return

Inkluderer en metodes signatur returtypen i Java?

1. Oversigt

Metodesignaturen er kun en delmængde af hele metodedefinitionen i Java. Således kan signaturens nøjagtige anatomi forårsage forvirring.

I denne øvelse lærer vi elementerne i metodesignaturen og dens implikationer i Java-programmering.

2. Metode signatur

Metoder i Java understøtter overbelastning, hvilket betyder, at flere metoder med samme navn kan defineres i samme klasse eller hierarki af klasser. Derfor skal compileren være i stand til statisk at binde den metode, klientkoden henviser til. Af denne grund identificerer metodesignaturen entydigt hver metode .

Ifølge Oracle består metoden signatur af navne- og parametertyperne . Derfor er alle de andre elementer i metodens erklæring, såsom modifikatorer, returtype, parameternavne, undtagelsesliste og brødtekst, ikke en del af signaturen.

Lad os se nærmere på metodeoverbelastning, og hvordan det relaterer sig til metodesignaturer.

3. Overbelastningsfejl

Lad os overveje følgende kode:

public void print() {
    System.out.println("Signature is: print()");
}

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

Som vi kan se, kompileres koden, da metoderne har forskellige parametertypelister. Faktisk kan compileren deterministisk binde ethvert kald til det ene eller det andet.

Lad os nu teste, om det er lovligt at overbelaste ved at tilføje følgende metode:

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

Når vi kompilerer, får vi fejlen "metoden er allerede defineret i klassen". Det beviser, at metoden returtype ikke er en del af metodesignaturen .

Lad os prøve det samme med modifikatorer:

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

Vi ser stadig den samme "metode er allerede defineret i klassen"-fejl. Derfor er metoden signatur ikke afhængig af modifikatorer .

Overbelastning ved at ændre kastede undtagelser kan testes ved at tilføje:

public void print() throws IllegalStateException { 
    System.out.println("Signature is: print()");
    throw new IllegalStateException();
}

Igen ser vi fejlen "metoden er allerede defineret i klassen", hvilket indikerer, at kast-erklæringen ikke kan være en del af signaturen .

Den sidste ting vi kan teste er, om ændring af parameternavnene tillader overbelastning. Lad os tilføje følgende metode:

public void print(int anotherParameter) { 
    System.out.println("Signature is: print(int)");
}

Som forventet får vi den samme kompileringsfejl. Det betyder, at parameternavne ikke påvirker metodesignaturen .

3. Generisk og typesletning

Med generiske parametreændrer type sletning den effektive signatur . Faktisk kan det forårsage en kollision med en anden metode, der bruger den øvre grænse for den generiske type i stedet for den generiske token.

Lad os overveje følgende kode:

public class OverloadingErrors<T extends Serializable> {

    public void printElement(T t) {
        System.out.println("Signature is: printElement(T)");
    }

    public void printElement(Serializable o) {
        System.out.println("Signature is: printElement(Serializable)");
    }
}

Selvom signaturerne ser anderledes ud, kan compileren ikke statisk binde den korrekte metode efter typesletning.

Vi kan se compileren erstatte T med den øvre grænse, Serialiserbar, på grund af typesletning. Således kolliderer det med metoden, der eksplicit bruger Serialiserbar .

Vi ville se det samme resultat med basistypen Objekt når den generiske type ikke er bundet.

4. Parameterlister og polymorfi

Metodesignaturen tager højde for de nøjagtige typer. Det betyder, at vi kan overbelaste en metode, hvis parametertype er en underklasse eller superklasse.

Vi skal dog være særligt opmærksomme, da statisk binding vil forsøge at matche ved hjælp af polymorfi, auto-boksning og typepromovering .

Lad os tage et kig på følgende kode:

public Number sum(Integer term1, Integer term2) {
    System.out.println("Adding integers");
    return term1 + term2;
}

public Number sum(Number term1, Number term2) {
    System.out.println("Adding numbers");
    return term1.doubleValue() + term2.doubleValue();
}

public Number sum(Object term1, Object term2) {
    System.out.println("Adding objects");
    return term1.hashCode() + term2.hashCode();
}

Ovenstående kode er helt lovlig og vil kompilere. Der kan opstå forvirring, når vi kalder disse metoder, da vi ikke kun skal kende den nøjagtige metodesignatur, vi kalder, men også hvordan Java statisk binder baseret på de faktiske værdier.

Lad os undersøge et par metodekald, der ender med at være bundet til sum(heltal, heltal) :

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

Til det første kald har vi de nøjagtige parametertyper Heltal, Heltal. Ved det andet opkald bokser Java automatisk int til Heltal for os. Til sidst vil Java transformere byteværdien 0x1 til int ved hjælp af type forfremmelse og derefter auto-boks det til Heltal.

På samme måde har vi følgende kald, der binder til sum(tal, tal) :

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

Ved det første opkald har vi dobbelt værdier, der automatisk indrammes til Dobbelt. Og så, ved hjælp af polymorfi, Dobbelt matcher Nummer. På samme måde Flyd matcher Nummer til det andet opkald.

Lad os se, at begge Flyder og Dobbelt arv fra Nummer og Objekt. Standardbindingen er dog til Nummer . Dette skyldes det faktum, at Java automatisk matcher de nærmeste supertyper, der matcher en metodesignatur.

Lad os nu overveje følgende metodekald:

obj.sum(2, "John");

I dette eksempel har vi en int til Heltal auto-boks for den første parameter. Der er dog ingen sum(heltal, streng) overbelastning for dette metodenavn. Som følge heraf vil Java køre gennem alle parameter-supertyperne for at caste fra den nærmeste forælder til Objekt indtil den finder et match. I dette tilfælde binder den til sum(Objekt, Objekt).

For at ændre standardbindingen kan vi bruge eksplicit parametercasting som følger:

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

5. Vararg-parametre

Lad os nu vende vores opmærksomhed mod, hvordan varargs påvirke metodens effektive signatur og statisk binding.

Her har vi en overbelastet metode, der bruger varargs :

public Number sum(Object term1, Object term2) {
    System.out.println("Adding objects");
    return term1.hashCode() + term2.hashCode();
}

public Number sum(Object term1, Object... term2) {
    System.out.println("Adding variable arguments: " + term2.length);
    int result = term1.hashCode();
    for (Object o : term2) {
        result += o.hashCode();
    }
    return result;
}

Så hvad er de effektive signaturer af metoderne? Vi har allerede set den sum(Objekt, Objekt) er signaturen for den første. Variable argumenter er i det væsentlige arrays, så den effektive signatur for sekundet efter kompilering er sum(Object, Object[]).

Et vanskeligt spørgsmål er, hvordan kan vi vælge metodebinding, når vi kun har to parametre?

Lad os overveje følgende opkald:

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

Det første kald vil naturligvis binde til sum(Object, Object) og den anden til sum(Objekt, Objekt[]). For at tvinge Java til at kalde den anden metode med to objekter, skal vi indpakke den i et array som i det tredje kald.

Den sidste ting at bemærke her er, at erklæringen af ​​følgende metode vil kollidere med vararg-versionen:

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

6. Konklusion

I denne øvelse lærte vi, at metodesignaturerne består af navnet og listen over parametertyper. Modifikatorerne, returtypen, parameternavnene og undtagelseslisten kan ikke skelne mellem overbelastede metoder og er derfor ikke en del af signaturen.

Vi har også set på, hvordan type sletning og varargs skjuler den effektive metodesignatur, og hvordan vi kan tilsidesætte Javas statiske metodebinding.

Som sædvanlig er alle kodeeksemplerne vist i denne artikel tilgængelige over på GitHub.


Java tag