Java-käyttöliittymät selitetty esimerkkien avulla

Liitännät

Java-käyttöliittymä on vähän kuin luokka, mutta sillä on merkittävä ero: an interfacevoi olla vain metodin allekirjoituksia, kenttiä ja oletusmenetelmiä. Java 8: sta lähtien voit luoda oletusmenetelmiä. Seuraavassa lohkossa näet esimerkin käyttöliittymästä:

public interface Vehicle { public String licensePlate = ""; public float maxVel public void start(); public void stop(); default void blowHorn(){ System.out.println("Blowing horn"); } }

Yllä oleva käyttöliittymä sisältää kaksi kenttää, kaksi menetelmää ja oletusmenetelmän. Yksinomaan siitä ei ole paljon hyötyä, mutta niitä käytetään yleensä luokkien kanssa. Miten? Yksinkertainen, sinun on varmistettava, että joku luokittelee implementssen.

public class Car implements Vehicle { public void start() { System.out.println("starting engine..."); } public void stop() { System.out.println("stopping engine..."); } }

Nyt on olemassa perussääntö : Luokan on toteutettava kaikki käyttöliittymän menetelmät. Menetelmillä on oltava täsmälleen sama allekirjoitus (nimi, parametrit ja poikkeukset) kuin käyttöliittymässä kuvataan. Luokan ei kuitenkaan tarvitse ilmoittaa kenttiä, vain menetelmät.

Liitännän esiintymät

Kun olet luonut Java-luokan, johon implementstahansa käyttöliittymään, objekti-ilmentymään voidaan viitata käyttöliittymän esiintymänä. Tämä käsite on samanlainen kuin perintöpikeen.

// following our previous example Vehicle tesla = new Car(); tesla.start(); // starting engine ...

Liitäntä ei voi sisältää konstruktorimenetelmiä. Siksi et voi luoda itse käyttöliittymän esiintymää. Sinun on luotava jonkin luokan ilmentymä, joka toteuttaa käyttöliittymän viitatakseen siihen.

Ajattele käyttöliittymiä tyhjänä sopimuslomakkeena tai mallina.

Mitä voit tehdä tällä ominaisuudella? Polymorfismi! Voit käyttää vain rajapintoja viittaamaan objektiinstansseihin!

class Truck implements Vehicle { public void start() { System.out.println("starting truck engine..."); } public void stop() { System.out.println("stopping truck engine..."); } } class Starter { // static method, can be called without instantiating the class public static void startEngine(Vehicle vehicle) { vehicle.start(); } } Vehicle tesla = new Car(); Vehicle tata = new Truck(); Starter.startEngine(tesla); // starting engine ... Starter.startEngine(tata); // starting truck engine ...

Mutta entä useita käyttöliittymiä?

Kyllä, voit toteuttaa useita liitäntöjä yhdessä luokassa. Kun luokkien sisäisessä perinnössä sinulla oli oikeus periä vain yksi luokka, tässä voit laajentaa mitä tahansa rajapintoja. Mutta älä unohda toteuttaa kaikkia liitäntöjen kaikkia menetelmiä, muuten kokoaminen epäonnistuu!

public interface GPS { public void getCoordinates(); } public interface Radio { public void startRadio(); public void stopRadio(); } public class Smartphone implements GPS,Radio { public void getCoordinates() { // return some coordinates } public void startRadio() { // start Radio } public void stopRadio() { // stop Radio } }

Joitakin käyttöliittymien ominaisuuksia

  • Voit sijoittaa muuttujia käyttöliittymään, vaikka se ei ole järkevä päätös, koska luokilla ei ole sidottu samaa muuttujaa. Lyhyesti sanottuna, vältä muuttujien sijoittamista!
  • Kaikki liitännän muuttujat ja menetelmät ovat julkisia, vaikka jätät publicavainsanan pois .
  • Liitäntä ei voi määrittää tietyn menetelmän toteutusta. Se on luokkien tehtävä tehdä se. Vaikka on ollut äskettäinen poikkeus (katso alla).
  • Jos luokka toteuttaa useita rajapintoja, menetelmän allekirjoituksen päällekkäisyys on etäinen. Koska Java ei salli useita menetelmiä samalla allekirjoituksella, se voi johtaa ongelmiin. Katso tämä kysymys saadaksesi lisätietoja.

Liitännän oletusmenetelmät

Ennen Java 8: ta meillä ei ollut mitään tapaa ohjata rajapintaa tietyn menetelmän toteuttamiseksi. Tämä aiheuttaa paljon sekaannusta ja koodinvaihtoa, jos käyttöliittymän määritelmää muutetaan yhtäkkiä.

Oletetaan, että kirjoitit avoimen lähdekoodin kirjaston, joka sisältää käyttöliittymän. Oletetaan, että asiakkaasi eli käytännössä kaikki kehittäjät ympäri maailmaa käyttävät sitä voimakkaasti ja ovat onnellisia. Nyt joudut päivittämään kirjaston lisäämällä käyttöliittymään uuden menetelmän määritelmän uuden ominaisuuden tukemiseksi. Mutta se rikkoisi kaikki koontiversiot, koska kaikkien kyseisen käyttöliittymän toteuttavien luokkien on muututtava nyt. Mikä katastrofi!

Onneksi Java 8 tarjoaa nyt defaultmenetelmiä rajapinnoille. defaultMenetelmä voi olla omat täytäntöönpanon suoraan käyttöliittymässä! Joten jos luokka ei ota käyttöön oletusmenetelmää, kääntäjä ottaa käyttöön käyttöliittymässä mainitun toteutuksen. Hienoa, eikö olekin? Joten kirjastossasi voit lisätä rajapintoihin minkä tahansa määrän oletusmenetelmiä pelkäämättä rikkoa mitään!

public interface GPS { public void getCoordinates(); default public void getRoughCoordinates() { // implementation to return coordinates from rough sources // such as wifi & mobile System.out.println("Fetching rough coordinates..."); } } public interface Radio { public void startRadio(); public void stopRadio(); } public class Smartphone implements GPS,Radio { public void getCoordinates() { // return some coordinates } public void startRadio() { // start Radio } public void stopRadio() { // stop Radio } // no implementation of getRoughCoordinates() } Smartphone motoG = new Smartphone(); motog.getRoughCoordinates(); // Fetching rough coordinates...

Mutta mitä tapahtuu, jos kahdella rajapinnalla on sama menetelmäallekirjoitus?

Mahtava kysymys. Siinä tapauksessa, jos et tarjoa toteutusta luokassa, huono kääntäjä hämmentyy ja yksinkertaisesti epäonnistuu! Sinun on myös annettava oletusmenetelmän toteutus luokan sisällä. On myös hieno tapa superkutsua haluamaasi toteutusta:

public interface Radio { // public void startRadio(); // public void stopRadio(); default public void next() { System.out.println("Next from Radio"); } } public interface MusicPlayer { // public void start(); // public void pause(); // public void stop(); default public void next() { System.out.println("Next from MusicPlayer"); } } public class Smartphone implements Radio, MusicPlayer { public void next() { // Suppose you want to call MusicPlayer next MusicPlayer.super.next(); } } Smartphone motoG = new Smartphone(); motoG.next(); // Next from MusicPlayer

Staattiset menetelmät rajapinnoissa

Java 8: n uutuus on myös mahdollisuus lisätä staattisia menetelmiä rajapintoihin. Rajapintojen staattiset menetelmät ovat lähes identtisiä konkreettisten luokkien staattisten menetelmien kanssa. Ainoa suuri ero on se, että staticmenetelmiä ei peritä luokissa, jotka toteuttavat käyttöliittymän. Tämä tarkoittaa, että käyttöliittymään viitataan, kun kutsutaan staattista menetelmää, ei luokkaa, joka toteuttaa sen.

interface MusicPlayer { public static void commercial(String sponsor) { System.out.println("Now for a message brought to you by " + sponsor); } public void play(); } class Smartphone implements MusicPlayer { public void play() { System.out.println("Playing from smartphone"); } } class Main { public static void main(String[] args) { Smartphone motoG = new Smartphone(); MusicPlayer.commercial("Motorola"); // Called on interface not on implementing class // motoG.commercial("Motorola"); // This would cause a compilation error } }

Käyttöliittymän periminen

Java-käyttöliittymässä on myös mahdollista periä toinen käyttöliittymä käyttämällä, arvasit sitä, extendsavainsanaa:

public interface Player { public void start(); public void pause(); public void stop(); } public interface MusicPlayer extends Player { default public void next() { System.out.println("Next from MusicPlayer"); } }

Tämä tarkoittaa, että luokan toteuttavan MusicPlayerkäyttöliittymän on toteutettava kaikki menetelmät MusicPlayersekä Player:

public class SmartPhone implements MusicPlayer { public void start() { System.out.println("start"); } public void stop() { System.out.println("stop"); } public void pause() { System.out.println("pause"); } }

Joten nyt sinulla on hyvä käsitys Java-rajapinnoista! Tutustu abstrakteihin luokkiin ja katso, miten Java antaa sinulle vielä yhden tavan määritellä sopimuksia.