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.

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

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 {}


Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow