Statische en standaardmethoden in interfaces in Java

1. Overzicht

Java 8 bracht een paar gloednieuwe functies naar de tafel, waaronder lambda-expressies, functionele interfaces, methodeverwijzingen, streams, optioneel en statisch en standaard methoden in interfaces.

Sommigen van hen zijn al behandeld in dit artikel. Niettemin, statisch en standaard methoden in interfaces verdienen op zichzelf een diepere kijk.

In dit artikel gaan we dieper in op hoe te gebruiken statisch en standaard methoden in interfaces en doorloop enkele use-cases waar ze nuttig kunnen zijn.

2. Waarom standaardmethoden in interfaces nodig zijn

Net als gewone interfacemethoden, standaardmethoden zijn impliciet openbaar - het is niet nodig om het openbaar modificator.

In tegenstelling tot reguliere interfacemethoden, zijn ze dat wel verklaard met de standaard trefwoord aan het begin van de methodehandtekening, en zij zorg voor een implementatie.

Laten we een eenvoudig voorbeeld bekijken:

openbare interface MyInterface {// reguliere interface methoden default void defaultMethod () {// standaard methode implementatie}}

De reden waarom standaard methoden zijn opgenomen in de Java 8-release is vrij duidelijk.

In een typisch ontwerp op basis van abstracties, waar een interface een of meerdere implementaties heeft, zullen alle implementaties worden gedwongen om deze ook te implementeren als er een of meer methoden aan de interface worden toegevoegd. Anders gaat het ontwerp gewoon kapot.

Standaardinterfacemethoden zijn een efficiënte manier om met dit probleem om te gaan. Ze stellen ons in staat om nieuwe methoden toe te voegen aan een interface die automatisch beschikbaar zijn in de implementaties. Het is dus niet nodig om de implementatieklassen te wijzigen.

Op deze manier, achterwaartse compatibiliteit blijft keurig behouden zonder de uitvoerders te hoeven refactoren.

3. Standaardinterfacemethoden in actie

Om de functionaliteit van standaard interface-methoden, laten we een eenvoudig voorbeeld maken.

Zeg dat we een naïef zijn Voertuig interface en slechts één implementatie. Er kunnen er meer zijn, maar laten we het zo simpel houden:

openbare interface Voertuig {String getBrand (); String speedUp (); Tekenreeks slowDown (); standaard String turnAlarmOn () {return "Het voertuigalarm inschakelen."; } default String turnAlarmOff () {return "Het voertuigalarm uitschakelen."; }}

En laten we de implementatieklasse schrijven:

public class Car implementeert Vehicle {private String brand; // constructors / getters @Override public String getBrand () {return brand; } @Override public String speedUp () {return "De auto versnelt."; } @Override public String slowDown () {return "De auto vertraagt."; }} 

Laten we tot slot een typisch definiëren hoofd class, die een instantie van Auto en noemt zijn methoden:

public static void main (String [] args) {Voertuig auto = nieuwe auto ("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 ()); }

Let op hoe de standaard methoden turnAlarmOn () en turnAlarmOff () van onze Voertuig interface zijn automatisch beschikbaar in het Auto klasse.

Bovendien, als we op een gegeven moment besluiten om meer toe te voegen standaard methoden naar de Voertuig interface, blijft de applicatie werken en hoeven we de klasse niet te dwingen implementaties voor de nieuwe methoden te bieden.

Het meest typische gebruik van standaardmethoden in interfaces is om incrementeel extra functionaliteit aan een bepaald type te bieden zonder de implementatieklassen op te splitsen.

Bovendien kunnen ze worden gebruikt bieden extra functionaliteit rond een bestaande abstracte methode:

openbare interface Voertuig {// aanvullende interfacemethoden double getSpeed ​​(); standaard dubbele getSpeedInKMH (dubbele snelheid) {// conversie}}

4. Meerdere interface-overervingsregels

Standaardinterfacemethoden zijn inderdaad een aardige functie, maar er zijn enkele kanttekeningen die het vermelden waard zijn. Omdat Java klassen toestaat om meerdere interfaces te implementeren, is het belangrijk om te weten wat gebeurt er als een klasse verschillende interfaces implementeert die hetzelfde definiëren standaard methoden.

Laten we een nieuw scenario definiëren om dit scenario beter te begrijpen Alarm interface en refactor het Auto klasse:

publieke interface Alarm {standaard String turnAlarmOn () {return "Het alarm aanzetten."; } default String turnAlarmOff () {return "Turning the alarm off."; }}

Met deze nieuwe interface definieert hij zijn eigen set van standaard methoden, de Auto class zou beide implementeren Voertuig en Alarm:

openbare klasse Auto implementeert voertuig, alarm {// ...}

In dit geval, de code kan eenvoudigweg niet worden gecompileerd, omdat er een conflict is dat wordt veroorzaakt door overerving van meerdere interfaces (ook bekend als het diamantprobleem). De Auto klasse zou beide sets van standaard methoden. Welke moeten er dan worden gebeld?

Om deze dubbelzinnigheid op te lossen, moeten we expliciet een implementatie voorzien voor de methoden:

@Override public String turnAlarmOn () {// aangepaste implementatie} @Override public String turnAlarmOff () {// aangepaste implementatie}

We kunnen ook laat onze klas de standaard methoden van een van de interfaces.

Laten we eens kijken naar een voorbeeld dat de standaard methoden uit de Voertuig koppel:

@Override openbare String turnAlarmOn () {retourneer Vehicle.super.turnAlarmOn (); } @Override openbare String turnAlarmOff () {retourneer Vehicle.super.turnAlarmOff (); } 

Evenzo kunnen we de klasse de standaard methoden gedefinieerd binnen de Alarm koppel:

@Override openbare String turnAlarmOn () {terug Alarm.super.turnAlarmOn (); } @Override openbare String turnAlarmOff () {terug Alarm.super.turnAlarmOff (); } 

Bovendien is het zelfs mogelijk om de Auto klasse gebruiken beide sets standaardmethoden:

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

5. Statische interfacemethoden

Afgezien van het kunnen aangeven standaard methoden in interfaces, Java 8 stelt ons in staat om te definiëren en implementeren statisch methoden in interfaces.

Sinds statisch methoden behoren niet tot een bepaald object, ze maken geen deel uit van de API van de klassen die de interface implementeren, en dat moeten ze ook zijn aangeroepen met behulp van de interfacenaam voorafgaand aan de naam van de methode.

Om te begrijpen hoe statisch methoden werken in interfaces, laten we de Voertuig interface en voeg er een statisch hulpprogramma methode:

openbare interface Voertuig {// reguliere / standaardinterfacemethoden statisch int getHorsePower (int rpm, int torque) {return (rpm * torque) / 5252; }} 

Een statisch methode binnen een interface is identiek aan het definiëren van een in een klasse. Bovendien is een statisch methode kan worden aangeroepen binnen andere statisch en standaard methoden.

Stel nu dat we het aantal pk's van de motor van een bepaald voertuig willen berekenen. We noemen gewoon de getHorsePower () methode:

Vehicle.getHorsePower (2500, 480)); 

Het idee erachter statisch interfacemethoden is om een ​​eenvoudig mechanisme te bieden waarmee we dat kunnen de mate van cohesie vergroten van een ontwerp door gerelateerde methoden op één plek samen te brengen zonder een object te hoeven maken.

Ongeveer hetzelfde kan worden gedaan met abstracte klassen. Het belangrijkste verschil zit hem in het feit dat abstracte klassen kunnen constructors, toestand en gedrag hebben.

Bovendien maken statische methoden in interfaces het mogelijk om gerelateerde utiliteitsmethoden te groeperen, zonder dat er kunstmatige utiliteitsklassen hoeven te worden gemaakt die slechts tijdelijke aanduidingen zijn voor statische methoden.

6. Conclusie

In dit artikel hebben we het gebruik van statisch en standaard interfacemethoden in Java 8. Op het eerste gezicht ziet deze functie er misschien een beetje slordig uit, vooral vanuit een objectgeoriënteerd puristisch perspectief. Idealiter zouden interfaces geen gedrag moeten inkapselen en zouden ze alleen moeten worden gebruikt voor het definiëren van de openbare API van een bepaald type.

Als het echter gaat om het behouden van achterwaartse compatibiliteit met bestaande code, statisch en standaard methoden zijn een goede afweging.

En, zoals gewoonlijk, zijn alle codevoorbeelden die in dit artikel worden getoond, beschikbaar op GitHub.