Programmering

Grensesnitt i Java

Java-grensesnitt er forskjellige fra klasser, og det er viktig å vite hvordan du bruker deres spesielle egenskaper i Java-programmene dine. Denne veiledningen introduserer forskjellen mellom klasser og grensesnitt, og guider deg gjennom eksempler som viser hvordan du kan erklære, implementere og utvide Java-grensesnitt.

Du vil også lære hvordan grensesnittet har utviklet seg i Java 8, med tillegg av standard- og statiske metoder, og i Java 9 med de nye private metodene. Disse tilleggene gjør grensesnitt mer nyttige for erfarne utviklere. Dessverre slører de også linjene mellom klasser og grensesnitt, noe som gjør grensesnittprogrammering enda mer forvirrende for Java-nybegynnere.

last ned Få koden Last ned kildekoden for eksempel applikasjoner i denne opplæringen. Skapt av Jeff Friesen for JavaWorld.

Hva er et Java-grensesnitt?

An grensesnitt er et punkt der to systemer møtes og samhandler. For eksempel kan du bruke et automatgrensesnitt for å velge en vare, betale for den og motta en mat- eller drikkevare. Fra et programmeringsperspektiv sitter et grensesnitt mellom programvarekomponenter. Tenk på at et metodetittel (metodenavn, parameterliste og så videre) grensesnittet sitter mellom ekstern kode som kaller metoden og koden innenfor metoden som vil bli utført som et resultat av samtalen. Her er et eksempel:

System.out.println (gjennomsnitt (10, 15)); dobbelt gjennomsnitt (dobbelt x, dobbelt y) // grensesnitt mellom gjennomsnitt (10, 15) samtale og retur (x + y) / 2; {retur (x + y) / 2; }

Det som ofte er forvirrende for Java-nybegynnere, er at klasser også har grensesnitt. Som jeg forklarte i Java 101: Klasser og objekter i Java, er grensesnittet den delen av klassen som er tilgjengelig for kode utenfor den. En klasses grensesnitt består av en kombinasjon av metoder, felt, konstruktører og andre enheter. Vurder oppføring 1.

Oppføring 1. Kontoklassen og grensesnittet

klassekonto {privat strengnavn; privat langt beløp; Konto (strengnavn, langt beløp) {this.name = name; setAmount (beløp); } ugyldig innskudd (langt beløp) {this.beløp + = beløp; } String getName () {return name; } lang getAmount () {returbeløp; } ugyldig setAmount (langt beløp) {this.amount = beløp; }}

De Konto (strengnavn, langt beløp) konstruktør og ugyldig innskudd (langt beløp), String getName (), lang getAmount (), og ugyldig setAmount (langt beløp) metoder danner Regnskap klassens grensesnitt: de er tilgjengelige for ekstern kode. De privat strengnavn; og privat langt beløp; felt er utilgjengelige.

Mer om Java-grensesnitt

Hva kan du gjøre med grensesnitt i Java-programmene dine? Få oversikt med Jeffs seks roller i Java-grensesnittet.

En metodes kode, som støtter metodens grensesnitt, og den delen av en klasse som støtter klassens grensesnitt (for eksempel private felt) er kjent som metodens eller klassens gjennomføring. En implementering bør være skjult fra ekstern kode slik at den kan endres for å møte utviklende krav.

Når implementeringer blir eksponert, kan det oppstå gjensidig avhengighet mellom programvarekomponenter. For eksempel kan metodekode stole på eksterne variabler, og brukere av en klasse kan bli avhengige av felt som burde vært skjult. Dette kobling kan føre til problemer når implementeringer må utvikle seg (kanskje eksponerte felt må fjernes).

Java-utviklere bruker grensesnittets språkfunksjon for å abstrakte klassegrensesnitt, altså frakobling klasser fra brukerne sine. Ved å fokusere på Java-grensesnitt i stedet for klasser, kan du minimere antall referanser til klassenavn i kildekoden. Dette gjør det lettere å bytte fra en klasse til en annen (kanskje for å forbedre ytelsen) når programvaren din modnes. Her er et eksempel:

List navn = ny ArrayList () ugyldig utskrift (List navn) {// ...}

Dette eksemplet erklærer og initialiserer a navn felt som lagrer en liste med strengnavn. Eksemplet erklærer også en skrive ut() metode for å skrive ut innholdet i en liste over strenger, kanskje en streng per linje. For kortfattethet har jeg utelatt implementeringen av metoden.

Liste er et Java-grensesnitt som beskriver en sekvensiell samling av objekter. ArrayList er en klasse som beskriver en arraybasert implementering av Liste Java-grensesnitt. En ny forekomst av ArrayList klasse er oppnådd og tildelt Liste variabel navn. (Liste og ArrayList lagres i standard klassebiblioteket java.util pakke.)

Vinkelfester og generiske legemidler

Vinkelbrakettene (< og >) er en del av Java's generiske funksjonssett. De indikerer det navn beskriver en liste over strenger (bare strenger kan lagres i listen). Jeg introduserer generikk i en fremtidig Java 101-artikkel.

Når klientkode samhandler med navn, vil den påkalle de metodene som er erklært av Liste, og som er implementert av ArrayList. Klientkoden vil ikke samhandle direkte med ArrayList. Som et resultat vil ikke klientkoden brytes når en annen implementeringsklasse, for eksempel LinkedList, kreves:

Listenavn = ny LinkedList () // ... ugyldig utskrift (List navn) {// ...}

Fordi det skrive ut() metoden parameter type er Liste, implementeringen av denne metoden trenger ikke å endres. Imidlertid, hvis typen hadde vært ArrayList, typen må endres til LinkedList. Hvis begge klassene skulle erklære sine egne unike metoder, må du kanskje endre betydelig skrive ut()implementering.

Frakobling Liste fra ArrayList og LinkedList lar deg skrive kode som er immun mot klasseimplementeringsendringer. Ved å bruke Java-grensesnitt kan du unngå problemer som kan oppstå ved å stole på implementeringsklasser. Denne frakoblingen er hovedårsaken til bruk av Java-grensesnitt.

Erklære Java-grensesnitt

Du erklærer et grensesnitt ved å følge en klasselignende syntaks som består av en overskrift etterfulgt av en kropp. I det minste består overskriften av nøkkelord grensesnitt etterfulgt av et navn som identifiserer grensesnittet. Kroppen starter med en åpen brace-karakter og ender med en tett brace. Mellom disse avgrensningene er konstante og metodehodeerklæringer:

grensesnitt identifikator {// grensesnitttekst}

Etter konvensjon er den første bokstaven i navnet på et grensesnitt store og store bokstaver (for eksempel Tegnes). Hvis et navn består av flere ord, blir den første bokstaven i hvert ord store bokstaver (for eksempel DrawableAndFillable). Denne navnekonvensjonen er kjent som CamelCasing.

Oppføring 2 erklærer et grensesnitt som heter Tegnes.

Oppføring 2. Et eksempel på et Java-grensesnitt

grensesnitt Drawable {int RED = 1; int GRØNN = 2; int BLÅ = 3; int SVART = 4; int HVIT = 5; ugyldig tegning (int farge); }

Grensesnitt i Java sitt standard klassebibliotek

Som en navngivningskonvensjon ender mange grensesnitt i Java sitt standard klassebibliotek med i stand suffiks. Eksempler inkluderer Kan kalles, Klonbar, Sammenlignelig, Formaterbar, Iterabel, Kjørbar, Serialiserbar, og Overførbar. Suffikset er imidlertid ikke obligatorisk; standard klassebiblioteket inkluderer grensesnittene CharSequence, Utklippstavle, Samling, Leder, Framtid, Iterator, Liste, Kart og mange andre.

Tegnes erklærer fem felt som identifiserer fargekonstanter. Dette grensesnittet erklærer også overskriften for en tegne() metoden som må kalles med en av disse konstantene for å spesifisere fargen som brukes til å tegne en disposisjon. (Å bruke heltallskonstanter er ikke en god ide fordi en heltallsverdi kan overføres til tegne(). Imidlertid er de nok i et enkelt eksempel.)

Standard- og feltoverskrift

Felter som er erklært i et grensesnitt er implisitt offentlig endelig statisk. Metodeoverskriftene til et grensesnitt er implisitt offentlig abstrakt.

Tegnes identifiserer en referansetype som spesifiserer hva du skal gjøre (tegne noe), men ikke hvordan du skal gjøre det. Implementeringsdetaljer sendes til klasser som implementerer dette grensesnittet. Forekomster av slike klasser er kjent som tegneserier fordi de vet hvordan de skal tegne seg selv.

Grensesnitt for markør og merking

Et grensesnitt med en tom kropp er kjent som en markørgrensesnitt eller a merkingsgrensesnitt. Grensesnittet eksisterer bare for å knytte metadata til en klasse. For eksempel, Klonbar (se Arv i Java, del 2) innebærer at forekomster av implementeringsklassen kan klones grunt. Når Gjenstands klone () metoden oppdager (via kjøretidstypeidentifikasjon) som den anropende instansens klasse implementerer Klonbar, kloner det grunt objektet.

Implementering av Java-grensesnitt

En klasse implementerer et grensesnitt ved å legge til Java-er redskaper nøkkelord etterfulgt av en kommaseparert liste over grensesnittnavn til klasseoverskriften, og ved å kode hver grensesnittmetode i klassen. Listing 3 presenterer en klasse som implementerer Listing 2 Tegnes grensesnitt.

Oppføring 3. Sirkel som implementerer det tegnesbare grensesnittet

klasse Sirkelredskaper Tegnbar {privat dobbel x, y, radius; Sirkel (dobbel x, dobbel y, dobbel radius) {this.x = x; this.y = y; denne.radius = radius; } @ Overstyr offentlig tomtrekk (int farge) {System.out.println ("Sirkel tegnet ved (" + x + "," + y + "), med radius" + radius + "og farge" + farge); } dobbel getRadius () {returradius; } dobbel getX () {return x; } doble getY () {return y; }}

Listing 3's Sirkel klasse beskriver en sirkel som et midtpunkt og en radius. I tillegg til å tilby en konstruktør og passende getter-metoder, Sirkel implementerer Tegnes grensesnitt ved å legge til implementerer tegnes til Sirkel topptekst og ved å overstyre (som angitt av @Overstyring kommentar) Tegness tegne() metodehode.

Oppføring 4 presenterer et annet eksempel: a Rektangel klasse som også implementerer Tegnes.

Oppføring 4. Implementering av det tegnesbare grensesnittet i en rektangel-kontekst

klasse Rektangelredskaper Tegnbare {private doble x1, y1, x2, y2; Rektangel (dobbelt x1, dobbelt y1, dobbelt x2, dobbelt y2) {dette.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2; } @ Overstyr offentlig tomromstegning (int-farge) {System.out.println ("Rektangel tegnet med øvre venstre hjørne ved (" + x1 + "," + y1 + ") og nedre høyre hjørne ved (" + x2 + "," + y2 + ") og farge" + farge); } dobbel getX1 () {retur x1; } dobbel getX2 () {retur x2; } dobbel getY1 () {return y1; } dobbel getY2 () {return y2; }}

Oppføring 4-er Rektangel klasse beskriver et rektangel som et par punkter som angir øvre venstre og nedre høyre hjørne av denne formen. Som med Sirkel, Rektangel gir en konstruktør og passende getter-metoder, og implementerer også Tegnes grensesnitt.

Overstyrende grensesnittmetodeoverskrifter

Kompilatoren rapporterer en feil når du prøver å kompilere en ikke-abstrakt klasse som inkluderer en redskaper grensesnittklausul, men overstyrer ikke alle grensesnittets metodeoverskrifter.

En grensesnitttypes dataverdier er objektene hvis klasser implementerer grensesnittet, og deres atferd er de som er spesifisert av grensesnittets metodeoverskrifter. Dette faktum innebærer at du kan tilordne et objekts referanse til en variabel av grensesnitttypen, forutsatt at objektets klasse implementerer grensesnittet. Oppføring 5 viser.

Listing 5. Aliasing Circle and Rectangle objects as Drawables

class Draw {public static void main (String [] args) {Drawable [] drawables = new Drawable [] {new Circle (10, 20, 15), new Circle (30, 20, 10), new Rectangle (5, 8 , 8, 9)}; for (int i = 0; i <drawables.length; i ++) drawables [i] .draw (Drawable.RED); }}

Fordi Sirkel og Rektangel implementere Tegnes, Sirkel og Rektangel gjenstander har Tegnes type i tillegg til klassetypene sine. Derfor er det lovlig å lagre hvert objekts referanse i en rekke Tegness. En sløyfe gjentas over denne matrisen, og påkaller hver Tegnes objektets tegne() metode for å tegne en sirkel eller et rektangel.

Forutsatt at Listing 2 er lagret i a Drawable.java kildefilen, som er i samme katalog som Circle.java, Rektangel.java, og Tegn.java kildefiler (som henholdsvis lagrer Listing 3, Listing 4 og Listing 5), kompilerer du disse kildefilene via en av følgende kommandolinjer:

javac Draw.java javac * .java

Kjør Tegne søknad som følger:

java Draw

Du bør følge følgende utdata:

Sirkel tegnet ved (10.0, 20.0), med radius 15.0, og farge 1 Sirkel tegnet ved (30.0, 20.0), med radius 10.0, og farge 1 Rektangel tegnet med øvre venstre hjørne ved (5.0, 8.0) og nedre høyre hjørne ved (8.0, 9.0) og farge 1

Merk at du også kan generere den samme produksjonen ved å spesifisere følgende hoved() metode:

public static void main (String [] args) {Circle c = new Circle (10, 20, 15); c.draw (Drawable.RED); c = ny sirkel (30, 20, 10); c.draw (Drawable.RED); Rektangel r = nytt rektangel (5, 8, 8, 9); r.draw (Drawable.RED); }

Som du kan se, er det kjedelig å gjenta hvert objekt tegne() metode. Videre gir dette ekstra bytekode til Tegnesin klassefil. Ved å tenke på Sirkel og Rektangel som Tegness, kan du utnytte en matrise og en enkel sløyfe for å forenkle koden. Dette er en ekstra fordel ved å designe kode for å foretrekke grensesnitt fremfor klasser.

Forsiktighet!

$config[zx-auto] not found$config[zx-overlay] not found