1. Evner

For bedre å forstå fordelene med grensesnitt og hvor de skal brukes, må vi snakke om noen mer abstrakte ting.

En klasse modellerer vanligvis et bestemt objekt. Et grensesnitt tilsvarer mindre objekter, og mer til deres evner eller roller.

Essensen av grensesnitt

For eksempel er ting som biler, sykler, motorsykler og hjul best representert som klasser og objekter. Men deres evner - som "Jeg kan bli ridd", "Jeg kan transportere folk", "Jeg kan stå" - presenteres bedre som grensesnitt. Her er noen eksempler:

Kode Beskrivelse
interface CanMove
{
   void move(String newLocation);
}
Tilsvarer evnen til å bevege seg
interface Rideable
{
   void ride(Passenger passenger);
}
Tilsvarer evnen til å bli ridd
interface CanTransport
{
   void addStuff(Object stuff);
   Object removeStuff();
}
Tilsvarer evnen til å transportere ting
class Wheel implements CanMove
{
   ...
}
Klassen Wheelkan bevege seg
class Car implements CanMove, Rideable, CanTransport
{
   ...
}
Klassen Carkan bevege seg, bli ridd og transportere ting
class Skateboard implements CanMove, Rideable
{
   ...
}
Klassen Skateboardkan bevege seg og bli ridd


2. Roller

Grensesnitt forenkler en programmerers liv. Svært ofte har et program tusenvis av objekter, hundrevis av klasser, men bare et par dusin grensesnitt , dvs. roller . Det er få roller, men det er mange måter å kombinere dem på (klasser).

Hele poenget er at du ikke trenger å skrive kode for i hver klasse for å samhandle med annenhver klasse. Du trenger bare å samhandle med rollene deres (grensesnitt).

Tenk deg at du er en kjæledyrtrener. Hvert av kjæledyrene du jobber med kan ha flere forskjellige evner. Du kommer i en vennlig krangel med naboen din om hvem sine kjæledyr som kan lage mest støy. For å avgjøre saken, stiller du bare opp alle kjæledyrene som kan "snakke", og du gir dem kommandoen: Snakk!

Du bryr deg ikke om hva slags dyr de er eller hvilke andre evner de har. Selv om de kan gjøre en trippel ryggsalto. I dette øyeblikket er du bare interessert i deres evne til å snakke høyt. Slik vil det se ut i kode:

Kode Beskrivelse
interface CanSpeak
{
   void speak();
}
Evnen CanSpeak. Dette grensesnittet forstår kommandoen til speak, noe som betyr at det har en tilsvarende metode.
class Cat implements CanSpeak
{
   void speak()
   {
      println("MEOW");
   }
}

class Dog implements CanSpeak
{
   void speak()
   {
      println("WOOF");
   }
}

class Fish
{
   ...
}
Dyr som har denne funksjonen.

For å lette forståelsen ga vi klassenavnene på engelsk. Dette er tillatt i Java, men det er svært uønsket.













Vår Fishhar ikke evnen til å snakke (implementerer ikke grensesnittet CanSpeak).

public static void main(String[] args)
{
   // Add all the animals to the list
   ArrayList pets = new ArrayList();
   pets.add(new Cat());
   pets.add(new Dog());
   pets.add(new Fish());

   // If the ability exists, then make a sound
   for(Object pet: pets)
   {
      if (pet instanceof CanSpeak)
      {
         CanSpeak loudmouth = (CanSpeak) pet;
         loudmouth.speak();
      }
   }
}
Og hvordan gir vi dem kommandoen?

Når antallet klasser i programmene dine når tusenvis, vil du ikke kunne leve uten grensesnitt. I stedet for å beskrive samspillet mellom tusenvis av klasser, er det nok å beskrive samspillet til noen få dusin grensesnitt - dette forenkler livet i stor grad.

Og når den kombineres med polymorfisme, er denne tilnærmingen generelt en knusende suksess.



3. defaultImplementeringen av grensesnittmetoder

Abstrakte klasser kan ha variabler og implementeringer av metoder, men de kan ikke ha multippel arv. Grensesnitt kan ikke ha variabler eller implementeringer av metoder, men som kan ha flere arv.

Situasjonen er uttrykt i følgende tabell:

Evne/egenskap Abstrakte klasser Grensesnitt
Variabler
Metodeimplementering
Multippel arv

Så, noen programmerere ønsket virkelig at grensesnitt skulle ha muligheten til å ha metodeimplementeringer. Men å ha muligheten til å legge til en metodeimplementering betyr ikke at en alltid vil bli lagt til. Legg den til hvis du vil. Eller hvis du ikke gjør det, så ikke gjør det.

I tillegg skyldes problemer med multippel arv først og fremst variabler. I alle fall var det det de bestemte og gjorde. Fra og med JDK 8 introduserte Java muligheten til å legge til metodeimplementeringer til grensesnitt.

Her er en oppdatert tabell (for JDK 8 og nyere):

Evne/egenskap Abstrakte klasser Grensesnitt
Variabler
Metodeimplementering
Multippel arv

Nå for abstrakte klasser så vel som grensesnitt, kan du deklarere metoder med eller uten implementering. Og dette er gode nyheter!

I abstrakte klasser må metoder uten implementering innledes med nøkkelordet abstract. Du trenger ikke legge til noe før metoder med en implementering. I grensesnitt er det motsatte. Hvis en metode ikke har en implementering, bør ingenting legges til. Men hvis det er en implementering, defaultmå søkeordet legges til.

For enkelhets skyld presenterer vi denne informasjonen i følgende lille tabell:

Evne/egenskap Abstrakte klasser Grensesnitt
Metoder uten implementering abstract
Metoder med en implementering default

Problem

Å bruke grensesnitt som har metoder kan i stor grad forenkle store klassehierarkier. For eksempel kan abstraktet InputStreamog OutputStreamklassene erklæres som grensesnitt! Dette lar oss bruke dem mye oftere og mye mer praktisk.

Men det finnes allerede titalls millioner (milliarder?) Java-klasser i verden. Og hvis du begynner å endre standardbiblioteker, kan du ødelegge noe. Som alt! 😛

For ikke å ødelegge eksisterende programmer og biblioteker ved et uhell, ble det bestemt at metodeimplementeringer i grensesnitt ville ha den laveste arveprioriteten .

For eksempel, hvis ett grensesnitt arver et annet grensesnitt som har en metode, og det første grensesnittet erklærer den samme metoden, men uten en implementering, vil ikke metodeimplementeringen fra det arvede grensesnittet nå det arvede grensesnittet. Eksempel:

interface Pet
{
   default void meow()
   {
      System.out.println("Meow");
   }
}

interface Cat extends Pet
{
   void meow(); // Here we override the default implementation by omitting an implementation
}

class Tom implements Cat
{
}

Koden vil ikke kompilere fordi Tomklassen ikke implementerer meow()metoden.