Java Language
Standaard methoden
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.
- De methode van de abstracte klasse heeft voorrang op de standaardmethode 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
- Class-methode heeft voorrang op Interface Default-methode
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 {}