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 Gjenstand
s 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) Tegnes
s 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 Tegnes
s. 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 Tegne
sin klassefil. Ved å tenke på Sirkel
og Rektangel
som Tegnes
s, 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.