Java >> Java tutorial >  >> Tag >> static

Statiske og standardmetoder i grænseflader i Java

1. Oversigt

Java 8 bragte et par helt nye funktioner til bordet, herunder lambda-udtryk, funktionelle grænseflader, metodereferencer, streams, valgfri og statisk og standard metoder i grænseflader.

Vi har allerede dækket et par af disse funktioner i en anden artikel. Ikke desto mindre statisk og standard metoder i grænseflader fortjener et dybere kig på egen hånd.

I dette selvstudie lærer vi hvordan du bruger statisk og standard metoder i grænseflader, og diskuter nogle situationer, hvor de kan være nyttige.

Yderligere læsning:

Private metoder i Java-grænseflader

Lær, hvordan du definerer private metoder i en grænseflade, og hvordan vi kan bruge dem fra både statiske og ikke-statiske sammenhænge.Læs mere →

Brug af en grænseflade vs. abstrakt klasse i Java

Lær, hvornår du skal bruge en grænseflade, og hvornår du skal bruge en abstrakt klasse i Java. Læs mere →

En guide til det statiske søgeord i Java

Lær om Java statiske felter, statiske metoder, statiske blokke og statiske indre klasser.Læs mere →

2. Hvorfor grænseflader har brug for standardmetoder

Ligesom almindelige grænseflademetoder er standardmetoder implicit offentlige; der er ingen grund til at angive offentligheden modifikator.

I modsætning til almindelige grænseflademetoder erklærer vi dem med standard søgeord i begyndelsen af ​​metodesignaturen , og de leverer en implementering .

Lad os se på et simpelt eksempel:

public interface MyInterface {
    
    // regular interface methods
    
    default void defaultMethod() {
        // default method implementation
    }
}

Grunden til, at Java 8-udgivelsen inkluderede standard metoder er ret indlysende.

I et typisk design baseret på abstraktioner, hvor en grænseflade har en eller flere implementeringer, hvis en eller flere metoder føjes til grænsefladen, vil alle implementeringerne også blive tvunget til at implementere dem. Ellers går designet bare i stykker.

Standardgrænseflademetoder er en effektiv måde at håndtere dette problem på. De giver os mulighed for at tilføje nye metoder til en grænseflade, der automatisk er tilgængelige i implementeringerne . Derfor behøver vi ikke at ændre implementeringsklasserne.

På denne måde er bagudkompatibiliteten pænt bevaret uden at skulle refaktorisere implementerne.

3. Standardgrænseflademetoder i aktion

For bedre at forstå funktionaliteten af ​​standard grænseflademetoder, lad os skabe et simpelt eksempel.

Antag, at vi har et naivt Køretøj interface og kun én implementering. Der kunne være flere, men lad os holde det så enkelt:

public interface Vehicle {
    
    String getBrand();
    
    String speedUp();
    
    String slowDown();
    
    default String turnAlarmOn() {
        return "Turning the vehicle alarm on.";
    }
    
    default String turnAlarmOff() {
        return "Turning the vehicle alarm off.";
    }
}

Lad os nu skrive implementeringsklassen:

public class Car implements Vehicle {

    private String brand;
    
    // constructors/getters
    
    @Override
    public String getBrand() {
        return brand;
    }
    
    @Override
    public String speedUp() {
        return "The car is speeding up.";
    }
    
    @Override
    public String slowDown() {
        return "The car is slowing down.";
    }
}

Lad os endelig definere en typisk main klasse, som opretter en forekomst af Bil og kalder dets metoder:

public static void main(String[] args) { 
    Vehicle car = new Car("BMW");
    System.out.println(car.getBrand());
    System.out.println(car.speedUp());
    System.out.println(car.slowDown());
    System.out.println(car.turnAlarmOn());
    System.out.println(car.turnAlarmOff());
}

Bemærk venligst hvordan standard metoder, turnAlarmOn() og turnAlarmOff(), fra vores Køretøj grænsefladen er automatisk tilgængelige i Bilen klasse .

Desuden, hvis vi på et tidspunkt beslutter at tilføje flere standard metoder til Køretøjet interface, vil applikationen stadig fortsætte med at fungere, og vi behøver ikke tvinge klassen til at levere implementeringer til de nye metoder.

Den mest almindelige brug af grænsefladestandardmetoder er at trinvist give yderligere funktionalitet til en given type uden at nedbryde implementeringsklasserne.

Derudover kan vi bruge dem til at give yderligere funktionalitet omkring en eksisterende abstrakt metode :

public interface Vehicle {
    
    // additional interface methods 
    
    double getSpeed();
    
    default double getSpeedInKMH(double speed) {
       // conversion      
    }
}

4. Arvregler for flere grænseflader

Standardgrænseflademetoder er en ret fin funktion, men der er nogle forbehold, der er værd at nævne. Da Java tillader klasser at implementere flere grænseflader, er det vigtigt at vide, hvad der sker, når en klasse implementerer flere grænseflader, der definerer den samme standard metoder .

For bedre at forstå dette scenarie, lad os definere en nyalarm interface og refaktorer Bilen klasse:

public interface Alarm {

    default String turnAlarmOn() {
        return "Turning the alarm on.";
    }
    
    default String turnAlarmOff() {
        return "Turning the alarm off.";
    }
}

Med denne nye grænseflade definerer den sit eget sæt standard metoder, Bilen klasse ville implementere både Køretøj og Alarm :

public class Car implements Vehicle, Alarm {
    // ...
}

I dette tilfælde kompileres koden simpelthen ikke, da der er en konflikt forårsaget af arv fra flere grænseflader (a.k.a Diamantproblemet). Bilen klasse ville arve begge sæt standard metoder. Så hvilke skal vi ringe til?

For at løse denne tvetydighed skal vi eksplicit levere en implementering af metoderne:

@Override
public String turnAlarmOn() {
    // custom implementation
}
    
@Override
public String turnAlarmOff() {
    // custom implementation
}

Vi kan også få vores klasse til at bruge standarden metoder for en af ​​grænsefladerne .

Lad os se et eksempel, der bruger standard metoder fra Køretøjet grænseflade:

@Override
public String turnAlarmOn() {
    return Vehicle.super.turnAlarmOn();
}

@Override
public String turnAlarmOff() {
    return Vehicle.super.turnAlarmOff();
}

På samme måde kan vi få klassen til at bruge standard metoder defineret i Alarm grænseflade:

@Override
public String turnAlarmOn() {
    return Alarm.super.turnAlarmOn();
}

@Override
public String turnAlarmOff() {
    return Alarm.super.turnAlarmOff();
}

Det er endda muligt at lave bilen klasse bruge begge sæt standardmetoder :

@Override
public String turnAlarmOn() {
    return Vehicle.super.turnAlarmOn() + " " + Alarm.super.turnAlarmOn();
}
    
@Override
public String turnAlarmOff() {
    return Vehicle.super.turnAlarmOff() + " " + Alarm.super.turnAlarmOff();
}

5. Statiske grænseflademetoder

Ud over at erklære standard metoder i grænseflader, Java 8 giver os også mulighed for at definere og implementere statisk metoder i grænseflader .

Siden statisk metoder hører ikke til et bestemt objekt, de er ikke en del af API'et for klasserne, der implementerer grænsefladen; derfor skal de kaldes ved at bruge grænsefladenavnet foran metodenavnet .

For at forstå, hvor statisk metoder fungerer i grænseflader, lad os refaktorisere Køretøjet grænseflade og tilføje en statisk hjælpemetode til det:

public interface Vehicle {
    
    // regular / default interface methods
    
    static int getHorsePower(int rpm, int torque) {
        return (rpm * torque) / 5252;
    }
}

Definition af en statisk metode inden for en grænseflade er identisk med at definere en i en klasse. Desuden en statisk metode kan påberåbes inden for andre statiske og standard metoder.

Lad os antage, at vi ønsker at beregne hestekræfterne af et givet køretøjs motor. Vi kalder bare getHorsePower() metode:

Vehicle.getHorsePower(2500, 480));

Idéen bag static grænseflademetoder er at give en simpel mekanisme, der giver os mulighed for at øge graden af ​​sammenhæng af et design ved at sammensætte relaterede metoder ét enkelt sted uden at skulle skabe et objekt.

Det samme kan stort set gøres med abstrakte klasser. Den største forskel er, at abstrakte klasser kan have konstruktører, tilstand og adfærd .

Desuden gør statiske metoder i grænseflader det muligt at gruppere relaterede hjælpemetoder uden at skulle oprette kunstige hjælpeklasser, der blot er pladsholdere for statiske metoder.

6. Konklusion

I denne artikel undersøgte vi i dybden brugen af ​​statisk og standard grænseflademetoder i Java 8. Ved første øjekast kan denne funktion se en smule sjusket ud, især fra et objektorienteret puristisk perspektiv. Ideelt set bør grænseflader ikke indkapsle adfærd, og vi bør kun bruge dem til at definere den offentlige API af en bestemt type.

Når det kommer til at opretholde bagudkompatibilitet med eksisterende kode, dog statisk og standard metoder er en god afvejning.

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


Java tag