Java Language
Standardmetoder
Sök…
Introduktion
Standardmetod introducerad i Java 8, gör det möjligt för utvecklare att lägga till nya metoder i ett gränssnitt utan att bryta de befintliga implementeringarna av detta gränssnitt. Det ger flexibilitet för att låta gränssnittet definiera en implementering som kommer att användas som standard när en klass som implementerar det gränssnittet inte tillhandahåller en implementering av den metoden.
Syntax
- public default void methodName () {/ * metodkod * /}
Anmärkningar
Standardmetoder
- Kan användas i ett gränssnitt för att introducera ett beteende utan att tvinga befintliga underklasser att implementera det.
- Kan åsidosättas av underklasser eller med ett subgränssnitt.
- Får inte åsidosätta metoder i java.lang.Object-klassen.
- Om en klass som implementerar mer än ett gränssnitt, ärver standardmetoder med identiska metodsignaturer från var och en av intefaces, måste den åsidosätta och ge sitt eget gränssnitt som om de inte var standardmetoder (som en del av att lösa flera arv).
- Även om de är avsedda att införa ett beteende utan att bryta befintliga implementeringar kommer befintliga underklasser med en statisk metod med samma metodsignatur som den nyligen införda standardmetoden att brytas. Detta är emellertid sant även om man introducerar en instansmetod i en superklass.
Statiska metoder
- Kan användas i ett gränssnitt, främst avsett att användas som verktygsmetod för standardmetoder.
- Kan inte åsidosättas av underklasser eller av ett subgränssnitt (är dold för dem). Men som nu är fallet med statiska metoder, kan varje klass eller gränssnitt ha sina egna.
- Får inte åsidosätta instansmetoder i java.lang.Object-klassen (som för närvarande också är fallet för underklasser).
Nedan följer en tabell som sammanfattar interaktionen mellan underklass och superklass.
- | SUPER_CLASS-förekomst METOD | SUPER_CLASS-statisk METOD |
---|---|---|
SUB_CLASS-förekomst METOD | overrides | genererar-kompileringsdeklaration-error |
SUB_CLASS-statisk METOD | genererar-kompileringsdeklaration-error | hudar |
Nedan följer en tabell som sammanfattar interaktionen mellan gränssnitt och implementeringsklass.
- | INTERFACE-DEFAULT-METOD | INTERFACE-statisk METOD |
---|---|---|
IMPL_CLASS-förekomst METOD | overrides | hudar |
IMPL_CLASS-statisk METOD | genererar-kompileringsdeklaration-error | hudar |
Referenser:
- http://www.journaldev.com/2752/java-8-interface-changes-static-method-default-method
- https://docs.oracle.com/javase/tutorial/java/IandI/override.html
Grundläggande användning av standardmetoder
/**
* 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" );
}
}
Följande uttalanden
new WithDefault().printString();
new OverrideDefault().printString();
Kommer att producera denna utgång:
default implementation
overridden implementation
Åtkomst till andra gränssnittsmetoder inom standardmetoden
Du kan också få tillgång till andra gränssnittsmetoder från din standardmetod.
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;
}
}
Följande uttalande kommer att skriva ut 3 :
System.out.println(new Sum().calculateSum());
Standardmetoder kan också användas tillsammans med statiska gränssnittsmetoder:
public interface Summable {
static int getA() {
return 1;
}
static int getB() {
return 2;
}
default int calculateSum() {
return getA() + getB();
}
}
public class Sum implements Summable {}
Följande uttalande kommer också att skriva ut 3:
System.out.println(new Sum().calculateSum());
Åtkomst till åsidosatta standardmetoder från implementering av klass
I klasserna super.foo()
i superklass. Om du vill kalla en standardimplementering från ett supergränssnitt måste du kvalificera super
med gränssnittsnamnet: 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
}
}
Varför använda standardmetoder?
Det enkla svaret är att det låter dig utveckla ett befintligt gränssnitt utan att bryta befintliga implementationer.
Till exempel har du Swim
som du publicerade för 20 år sedan.
public interface Swim {
void backStroke();
}
Vi gjorde ett bra jobb, vårt gränssnitt är mycket populärt, det finns många implementeringar av det runt om i världen och du har inte kontroll över deras källkod.
public class FooSwimmer implements Swim {
public void backStroke() {
System.out.println("Do backstroke");
}
}
Efter 20 år har du bestämt dig för att lägga till ny funktionalitet i gränssnittet, men det ser ut som att vårt gränssnitt är fruset eftersom det kommer att bryta befintliga implementationer.
Lyckligtvis introducerar Java 8 helt ny funktion som kallas standardmetod.
Vi kan nu lägga till en ny metod i Swim
gränssnittet.
public interface Swim {
void backStroke();
default void sideStroke() {
System.out.println("Default sidestroke implementation. Can be overridden");
}
}
Nu kan alla befintliga implementationer av vårt gränssnitt fortfarande fungera. Men viktigast av allt kan de implementera den nyligen tillagda metoden på sin egen tid.
En av de största orsakerna till denna förändring, och en av dess största användningsområden, är inom ramen för Java Collections. Oracle kunde inte lägga till en foreach
metod till det befintliga Iterable-gränssnittet utan att bryta all befintlig kod som implementerade Iterable. Genom att lägga till standardmetoder kommer den nuvarande implementerbara implementeringen att ärva standardimplementeringen.
Företräde för klass, abstrakt klass och gränssnittsmetod
Implementeringar i klasser, inklusive abstrakta deklarationer, har företräde framför alla standardgränssnitt.
- Abstrakt klassmetod har företräde framför Interface Standard Method .
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 {
}
Följande uttalande
new FooSwimmer().backStroke();
Kommer att producera
AbstractSwimmer.backStroke
- Klassmetod har företräde framför Interface Standard Method
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");
}
}
Följande uttalande
new FooSwimmer().backStroke();
Kommer att producera
FooSwimmer.backStroke
Standardmetod multipel arv kollision
Tänk på nästa exempel:
public interface A {
default void foo() { System.out.println("A.foo"); }
}
public interface B {
default void foo() { System.out.println("B.foo"); }
}
Här är två gränssnitt som förklarar default
foo
med samma signatur.
Om du försöker extend
dessa båda gränssnitt i det nya gränssnittet måste du välja två, eftersom Java tvingar dig att lösa denna kollision uttryckligen.
Först kan du förklara metod foo
med samma signatur som abstract
, vilket kommer att åsidosätta A
och B
beteende.
public interface ABExtendsAbstract extends A, B {
@Override
void foo();
}
Och när du kommer att implement
ABExtendsAbstract
i class
måste du tillhandahålla foo
implementering:
public class ABExtendsAbstractImpl implements ABExtendsAbstract {
@Override
public void foo() { System.out.println("ABImpl.foo"); }
}
Eller för det andra kan du tillhandahålla en helt ny default
. Du kan också återanvända kod för A
och B
foo
metoder genom att komma åt åsidosatta standardmetoder från att implementera klass .
public interface ABExtends extends A, B {
@Override
default void foo() { System.out.println("ABExtends.foo"); }
}
Och när du implement
ABExtends
i class
ABExtends
du not
tillhandahålla foo
implementering:
public class ABExtendsImpl implements ABExtends {}