Zoeken…


Invoering

Met de standaardmethode geïntroduceerd in Java 8 kunnen ontwikkelaars nieuwe methoden aan een interface toevoegen zonder de bestaande implementaties van deze interface te verbreken. Het biedt flexibiliteit zodat de interface een implementatie kan definiëren die als standaard wordt gebruikt wanneer een klasse die die interface implementeert, geen implementatie van die methode biedt.

Syntaxis

  • openbare standaard void methodName () {/ * method body * /}

Opmerkingen

Standaard methoden

  • Kan binnen een interface worden gebruikt om een gedrag te introduceren zonder bestaande subklassen te dwingen het te implementeren.
  • Kan worden overschreven door subklassen of door een subinterface.
  • Mag methoden in java.lang.Object-klasse niet overschrijven.
  • Als een klasse die meer dan één interface implementeert, standaardmethoden met identieke methodehandtekeningen overneemt van elk van de intefaces, dan moet deze de eigen interface vervangen of bieden als ware het geen standaardmethoden (als onderdeel van het oplossen van meervoudige overerving).
  • Hoewel bedoeld is om een gedrag te introduceren zonder bestaande implementaties te verbreken, zullen bestaande subklassen met een statische methode met dezelfde methodehandtekening als de nieuw geïntroduceerde standaardmethode nog steeds worden verbroken. Dit is echter ook waar in het geval van het introduceren van een instantiemethode in een superklasse.



Statische methoden

  • Kan worden gebruikt binnen een interface, voornamelijk bedoeld om te worden gebruikt als een hulpprogramma voor standaardmethoden.
  • Kan niet worden opgeheven door subklassen of door een subinterface (is voor hen verborgen). Maar zoals nu al het geval is met statische methoden, kan elke klasse of interface zijn eigen hebben.
  • Het is niet toegestaan om instantiemethoden in java.lang.Object-klasse te overschrijven (zoals momenteel ook het geval is voor subklassen).



Hieronder is een tabel met een samenvatting van de interactie tussen subklasse en superklasse.

- SUPER_CLASS aanleg-METHODE SUPER_CLASS-STATIC-METHODE
SUB_CLASS aanleg-METHODE overschrijvingen genereert-COMPILETIME-fout
SUB_CLASS-STATIC-METHODE genereert-COMPILETIME-fout huiden



Hieronder is een tabel met een samenvatting van de interactie tussen interface en implementatieklasse.

- INTERFACE-default-METHODE INTERFACE-STATIC-METHODE
IMPL_CLASS aanleg-METHODE overschrijvingen huiden
IMPL_CLASS-STATIC-METHODE genereert-COMPILETIME-fout huiden

Referenties:

  • http://www.journaldev.com/2752/java-8-interface-changes-static-method-default-method
  • https://docs.oracle.com/javase/tutorial/java/IandI/override.html

Basisgebruik van standaardmethoden

/**
 * Interface with default method
 */
public interface Printable {
    default void printString() {
        System.out.println( "default implementation" );
    }
}

/**
 * Class which falls back to default implementation of {@link #printString()}
 */
public class WithDefault
    implements Printable
{
}

/**
 * Custom implementation of {@link #printString()}
 */
public class OverrideDefault
    implements Printable {
    @Override
    public void printString() {
        System.out.println( "overridden implementation" );
    }
}

De volgende verklaringen

    new WithDefault().printString();
    new OverrideDefault().printString();

Zal deze output produceren:

default implementation
overridden implementation

Toegang tot andere interfacemethoden binnen de standaardmethode

U kunt ook toegang krijgen tot andere interfacemethoden vanuit uw standaardmethode.

public interface Summable {
    int getA();

    int getB();

    default int calculateSum() {
        return getA() + getB();
    }
}

public class Sum implements Summable {
    @Override
    public int getA() {
        return 1;
    }

    @Override
    public int getB() {
        return 2;
    }
}

De volgende verklaring zal 3 afdrukken:

System.out.println(new Sum().calculateSum());

Standaardmethoden kunnen ook worden gebruikt in combinatie met statische interfacemethoden:

public interface Summable {
    static int getA() {
        return 1;
    }

    static int getB() {
        return 2;
    }

    default int calculateSum() {
        return getA() + getB();
    }
}

public class Sum implements Summable {}

De volgende verklaring zal ook 3 afdrukken:

System.out.println(new Sum().calculateSum());

Toegang tot overschreven standaardmethoden van implementatieklasse

In klassen ziet super.foo() er alleen in superklassen uit. Als u wilt een standaard implementatie van een superinterface noemen, moet je te kwalificeren super met de naam interface: Fooable.super.foo() .

public interface Fooable {
    default int foo() {return 3;}
}

public class A extends Object implements Fooable {
    @Override
    public int foo() {
        //return super.foo() + 1; //error: no method foo() in java.lang.Object
        return Fooable.super.foo() + 1; //okay, returns 4
    }
}

Waarom standaardmethoden gebruiken?

Het eenvoudige antwoord is dat u hiermee een bestaande interface kunt ontwikkelen zonder bestaande implementaties te verbreken.

Je hebt bijvoorbeeld een Swim die je 20 jaar geleden hebt gepubliceerd.

public interface Swim {
    void backStroke();
}

We hebben het geweldig gedaan, onze interface is erg populair, er zijn veel implementaties over de hele wereld en je hebt geen controle over hun broncode.

public class FooSwimmer implements Swim {
    public void backStroke() {
         System.out.println("Do backstroke");
    }
}

Na 20 jaar hebt u besloten om nieuwe functionaliteit aan de interface toe te voegen, maar het lijkt erop dat onze interface is bevroren omdat deze bestaande implementaties zal doorbreken.

Gelukkig introduceert Java 8 een geheel nieuwe functie genaamd Standaardmethode.

We kunnen nu een nieuwe methode toevoegen aan de interface Swim .

public interface Swim {
    void backStroke();
    default void sideStroke() {
        System.out.println("Default sidestroke implementation. Can be overridden");
    }
}

Nu kunnen alle bestaande implementaties van onze interface nog steeds werken. Maar het belangrijkste is dat ze de nieuw toegevoegde methode in hun eigen tijd kunnen implementeren.

Een van de grootste redenen voor deze wijziging, en een van de grootste toepassingen ervan, ligt in het Java Collections-framework. Oracle kon geen foreach methode toevoegen aan de bestaande Iterable-interface zonder alle bestaande code te breken die Iterable implementeerde. Door standaardmethoden toe te voegen, neemt de bestaande Iterable-implementatie de standaardimplementatie over.

Voorrang voor klasse, abstracte klasse en interfacemethode

Implementaties in klassen, inclusief abstracte verklaringen, hebben voorrang op alle standaardinstellingen van de interface.

public interface Swim {
    default void backStroke() {
        System.out.println("Swim.backStroke");
    }
}

public abstract class AbstractSwimmer implements Swim {
    public void backStroke() {
        System.out.println("AbstractSwimmer.backStroke");
    }
}

public class FooSwimmer extends AbstractSwimmer {
}

De volgende verklaring

new FooSwimmer().backStroke();

Zal produceren

AbstractSwimmer.backStroke

public interface Swim {
    default void backStroke() {
        System.out.println("Swim.backStroke");
    }
}

public abstract class AbstractSwimmer implements Swim {
}

public class FooSwimmer extends AbstractSwimmer {
    public void backStroke() {
        System.out.println("FooSwimmer.backStroke");
    }
}

De volgende verklaring

new FooSwimmer().backStroke();

Zal produceren

FooSwimmer.backStroke

Standaardmethode meervoudige overervingbotsing

Overweeg het volgende voorbeeld:

public interface A {
    default void foo() { System.out.println("A.foo"); }
}

public interface B {
    default void foo() { System.out.println("B.foo"); }
}

Hier zijn twee interfaces die default foo verklaren met dezelfde handtekening.

Als u deze beide interfaces in de nieuwe interface wilt extend moet u kiezen uit twee, omdat Java u dwingt deze botsing expliciet op te lossen.

Eerst kun je methode foo verklaren met dezelfde handtekening als abstract , die het gedrag van A en B vervangt.

public interface ABExtendsAbstract extends A, B {
    @Override
    void foo();
}

En wanneer je ABExtendsAbstract in de class gaat implement , moet je foo implementatie bieden:

public class ABExtendsAbstractImpl implements ABExtendsAbstract {
    @Override
    public void foo() { System.out.println("ABImpl.foo"); }
}

Of ten tweede , u kunt een volledig nieuwe default . U kunt ook code van A en B foo methoden hergebruiken door toegang te krijgen tot overschreven standaardmethoden van de implementatieklasse .

public interface ABExtends extends A, B {
    @Override
    default void foo() { System.out.println("ABExtends.foo"); }
}

En wanneer u ABExtends in de class gaat implement ABExtends u not foo implementatie te bieden:

public class ABExtendsImpl implements ABExtends {}


Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow