Programmering

Grænseflader i Java

Java-grænseflader adskiller sig fra klasser, og det er vigtigt at vide, hvordan man bruger deres specielle egenskaber i dine Java-programmer. Denne vejledning introducerer forskellen mellem klasser og grænseflader og guider dig derefter gennem eksempler, der viser, hvordan du erklærer, implementerer og udvider Java-grænseflader.

Du lærer også, hvordan grænsefladen har udviklet sig i Java 8 med tilføjelse af standard- og statiske metoder og i Java 9 med de nye private metoder. Disse tilføjelser gør grænseflader mere nyttige for erfarne udviklere. Desværre slører de også linierne mellem klasser og grænseflader, hvilket gør programmering af grænseflader endnu mere forvirrende for Java-begyndere.

download Hent koden Download kildekoden for eksempel applikationer i denne vejledning. Oprettet af Jeff Friesen til JavaWorld.

Hvad er en Java-grænseflade?

En interface er et punkt, hvor to systemer mødes og interagerer. For eksempel kan du bruge en grænseflade til salgsautomat til at vælge en vare, betale for den og modtage en mad- eller drikkevare. Fra et programmeringsperspektiv sidder en grænseflade mellem softwarekomponenter. Overvej, at en metodehoved (metodens navn, parameterliste osv.) Grænseflade sidder mellem ekstern kode, der kalder metoden, og koden inden for den metode, der udføres som et resultat af opkaldet. Her er et eksempel:

System.out.println (gennemsnit (10, 15)); dobbelt gennemsnit (dobbelt x, dobbelt y) // interface mellem gennemsnit (10, 15) opkald og retur (x + y) / 2; {return (x + y) / 2; }

Hvad der ofte er forvirrende for Java-begyndere er, at klasser også har grænseflader. Som jeg forklarede i Java 101: Klasser og objekter i Java, er grænsefladen den del af klassen, der er tilgængelig for kode, der ligger uden for den. En klasses grænseflade består af en kombination af metoder, felter, konstruktører og andre enheder. Overvej at liste 1.

Notering 1. Kontoklassen og dens interface

klassekonto {privat strengnavn; privat langt beløb Konto (strengnavn, langt beløb) {this.name = navn; setAmount (beløb); } ugyldigt depositum (langt beløb) {this.amount + = beløb; } String getName () {return name; } lang getAmount () {returbeløb; } ugyldigt setAmount (langt beløb) {this.amount = beløb; }}

Det Konto (strengnavn, langt beløb) konstruktør og ugyldig depositum (langt beløb), String getName (), lang getAmount ()og ugyldigt setAmount (langt beløb) metoder danner Konto klassens grænseflade: de er tilgængelige for ekstern kode. Det privat strengnavn; og privat langt beløb felter er utilgængelige.

Mere om Java-grænseflader

Hvad kan du gøre med grænseflader i dine Java-programmer? Få et overblik med Jeffs seks roller i Java-grænsefladen.

En metodes kode, der understøtter metodens grænseflade, og den del af en klasse, der understøtter klassens grænseflade (såsom private felter), er kendt som metodens eller klassens implementering. En implementering skal være skjult for ekstern kode, så den kan ændres for at imødekomme udviklende krav.

Når implementeringer eksponeres, kan der opstå indbyrdes afhængighed mellem softwarekomponenter. For eksempel kan metoden kode stole på eksterne variabler, og en klasses brugere kan blive afhængige af felter, der burde have været skjult. Det her kobling kan føre til problemer, når implementeringer skal udvikle sig (måske skal eksponerede felter fjernes).

Java-udviklere bruger således interface-sprogfunktionen til at abstrakte klassegrænseflader afkobling klasser fra deres brugere. Ved at fokusere på Java-grænseflader i stedet for klasser, kan du minimere antallet af referencer til klassenavne i din kildekode. Dette letter skift fra en klasse til en anden (måske for at forbedre ydeevnen), når din software modnes. Her er et eksempel:

List names = new ArrayList () void print (List names) {// ...}

Dette eksempel erklærer og initialiserer a navne felt, der gemmer en liste med strengnavne. Eksemplet erklærer også en Print() metode til udskrivning af indholdet af en liste over strenge, måske en streng pr. linje. For kortfattethed har jeg udeladt metodens implementering.

Liste er en Java-grænseflade, der beskriver en sekventiel samling af objekter. ArrayList er en klasse, der beskriver en array-baseret implementering af Liste Java-grænseflade. En ny forekomst af ArrayList klasse opnås og tildeles Liste variabel navne. (Liste og ArrayList gemmes i standardklassebiblioteket java.util pakke.)

Vinkelbeslag og generiske lægemidler

Vinkelbeslagene (< og >) er en del af Java's generiske funktionssæt. De indikerer det navne beskriver en liste over strenge (kun strenge kan gemmes på listen). Jeg introducerer generics i en fremtidig Java 101-artikel.

Når klientkode interagerer med navne, vil den påberåbe sig de metoder, der er erklæret af Liste, og som er implementeret af ArrayList. Klientkoden interagerer ikke direkte med ArrayList. Som et resultat bryder klientkoden ikke, når en anden implementeringsklasse, f.eks LinkedList, er påkrævet:

List names = new LinkedList () // ... ugyldig udskrivning (List names) {// ...}

Fordi Print() metode parametertype er Liste, denne metodes implementering behøver ikke at ændre sig. Men hvis typen havde været ArrayList, typen skulle ændres til LinkedList. Hvis begge klasser skulle erklære deres egne unikke metoder, skal du muligvis ændre væsentligt Print()implementering.

Afkobling Liste fra ArrayList og LinkedList lader dig skrive kode, der er immun over for ændringer af klasseimplementering. Ved at bruge Java-grænseflader kan du undgå problemer, der kan opstå ved at stole på implementeringsklasser. Denne afkobling er hovedårsagen til brugen af ​​Java-grænseflader.

Erklæring om Java-grænseflader

Du erklærer en grænseflade ved at overholde en klasselignende syntaks, der består af en overskrift efterfulgt af en krop. Overskriften består som minimum af nøgleord interface efterfulgt af et navn, der identificerer grænsefladen. Kroppen starter med en åben brace-karakter og slutter med en tæt bøjle. Mellem disse afgrænsninger er konstaterede og metodehovederklæringer:

interface identifikator {// interface-krop}

Efter konvention er det første bogstav i et grænseflades navn store bogstaver, og efterfølgende bogstaver er små bogstaver (f.eks. Kan tegnes). Hvis et navn består af flere ord, er det første bogstav i hvert ord med store bogstaver (f.eks DrawableAndFillable). Denne navngivningskonvention er kendt som CamelCasing.

Liste 2 erklærer en grænseflade navngivet Kan tegnes.

Liste 2. Et eksempel på et Java-interface

grænseflade, der kan tegnes {int RED = 1; int GRØNN = 2; int BLÅ = 3; int SORT = 4; int HVID = 5; ugyldig tegning (int-farve); }

Grænseflader i Java's standard klassebibliotek

Som en navngivningskonvention slutter mange grænseflader i Java's standardklassebibliotek med i stand suffiks. Eksempler inkluderer Kan kaldes, Klonabel, Sammenlignelig, Formaterbar, Iterabel, Kan køres, Serialiserbarog Overførbar. Suffikset er dog ikke obligatorisk; standard klassebiblioteket inkluderer grænsefladerne CharSequence, Udklipsholder, Kollektion, Eksekutor, Fremtid, Iterator, Liste, Kort og mange andre.

Kan tegnes erklærer fem felter, der identificerer farvekonstanter. Denne grænseflade erklærer også overskriften for en tegne() metode, der skal kaldes med en af ​​disse konstanter for at specificere den farve, der bruges til at tegne en kontur. (Brug af heltalskonstanter er ikke en god idé, fordi enhver heltalsværdi kan overføres til tegne(). De er dog tilstrækkelige i et simpelt eksempel.)

Standard- og feltoverskrift-header

Felter, der er erklæret i en grænseflade, er implicit offentlig endelig statisk. En grænseflades metodeoverskrifter er implicit offentlig abstrakt.

Kan tegnes identificerer en referencetype, der specificerer, hvad man skal gøre (tegne noget), men ikke hvordan man gør det. Implementeringsoplysninger sendes til klasser, der implementerer denne grænseflade. Forekomster af sådanne klasser er kendt som trækbare, fordi de ved, hvordan de tegner sig selv.

Grænseflader til markør og tagging

En grænseflade med en tom krop er kendt som en markørgrænseflade eller a tagging interface. Interfacet findes kun for at knytte metadata til en klasse. For eksempel, Klonabel (se Arv i Java, del 2) indebærer, at forekomster af dens implementeringsklasse kan klones lavt. Hvornår Objekt's klon () metode registrerer (via identifikation af runtime-type), som den opkaldende instanss klasse implementerer Klonabel, kloner det grundigt objektet.

Implementering af Java-grænseflader

En klasse implementerer en grænseflade ved at tilføje Java'er redskaber nøgleord efterfulgt af en komma-adskilt liste over interface-navne til klasseoverskriften og ved at kode hver enkelt interface-metode i klassen. Listing 3 præsenterer en klasse, der implementerer Listing 2's Kan tegnes interface.

Listing 3. Cirkel, der implementerer Drawable-grænsefladen

klasse Cirkelredskaber Tegneserie {privat dobbelt x, y, radius; Cirkel (dobbelt x, dobbelt y, dobbelt radius) {this.x = x; this.y = y; denne.radius = radius; } @ Overstyr offentlig ugyldig tegning (int-farve) {System.out.println ("Cirkel tegnet ved (" + x + "," + y + ") med radius" + radius + "og farve" + farve); } dobbelt getRadius () {returradius; } dobbelt getX () {return x; } dobbelt getY () {returner y; }}

Liste 3's Cirkel klasse beskriver en cirkel som et midtpunkt og en radius. Ud over at give en konstruktør og passende gettermetoder, Cirkel implementerer Kan tegnes interface ved at tilføje redskaber, der kan tegnes til Cirkel header og ved at tilsidesætte (som angivet af @Override kommentar) Kan tegnes's tegne() metodehoved.

Listing 4 præsenterer et andet eksempel: a Rektangel klasse, der også implementerer Kan tegnes.

Fortegnelse 4. Implementering af Drawable-grænsefladen i en rektangelkontekst

klasse Rektangelværktøjer Tegneseribelt {privat dobbelt x1, y1, x2, y2; Rektangel (dobbelt x1, dobbelt y1, dobbelt x2, dobbelt y2) {dette.x1 = x1; this.y1 = y1; dette. x2 = x2; this.y2 = y2; } @ Overstyr offentlig ugyldig tegning (int-farve) {System.out.println ("Rektangel tegnet med øverste venstre hjørne ved (" + x1 + "," + y1 + ") og nederste højre hjørne ved (" + x2 + "," + y2 + ") og farve" + farve); } dobbelt getX1 () {return x1; } dobbelt getX2 () {return x2; } dobbelt getY1 () {returner y1; } dobbelt getY2 () {returner y2; }}

Liste 4's Rektangel klasse beskriver et rektangel som et par punkter, der angiver de øverste venstre og nederste højre hjørner af denne form. Som med Cirkel, Rektangel giver en konstruktør og egnede gettermetoder og implementerer også Kan tegnes interface.

Overordnede interface-overskrifter

Compileren rapporterer en fejl, når du forsøger at kompilere en ikke-abstrakt klasse, der inkluderer en redskaber interface-klausul, men tilsidesætter ikke alle grænseflades metodeoverskrifter.

En grænsefladetypes dataværdier er de objekter, hvis klasser implementerer grænsefladen, og hvis adfærd er dem, der er specificeret af grænseflades metodeoverskrifter. Denne kendsgerning indebærer, at du kan tildele et objekts reference til en variabel af interface-typen, forudsat at objektets klasse implementerer interface. Liste 5 viser.

Notering 5. Aliasing af cirkel- og rektangelobjekter som tegninger

klasse 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 Cirkel og Rektangel implementere Kan tegnes, Cirkel og Rektangel genstande har Kan tegnes skriv ud over deres klassetyper. Derfor er det lovligt at gemme hvert objekts reference i en række Kan tegness. En løkke gentager sig over denne matrix og påberåber hver Kan tegnes objekt tegne() metode til at tegne en cirkel eller et rektangel.

Antages det, at Listing 2 er gemt i en Tegneserie.java kildefil, som er i samme bibliotek som Cirkel.java, Rektangel.javaog Tegn.java kildefiler (som henholdsvis gemmer lister 3, lister 4 og lister 5), kompiler disse kildefiler via en af ​​følgende kommandolinjer:

javac Draw.java javac * .java

Kør Tegne ansøgning som følger:

java Draw

Du skal overholde følgende output:

Cirkel tegnet ved (10.0, 20.0), med radius 15.0 og farve 1 Cirkel tegnet ved (30.0, 20.0), med radius 10.0 og farve 1 Rektangel tegnet med øverste venstre hjørne ved (5.0, 8.0) og nederste højre hjørne ved (8.0, 9.0) og farve 1

Bemærk, at du også kunne generere den samme output ved at angive følgende hoved () metode:

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

Som du kan se, er det kedeligt at gentagne gange påberåbe sig hvert objekt tegne() metode. Desuden tilføjer dette ekstra bytekode til Tegneklassefil. Ved at tænke på Cirkel og Rektangel som Kan tegness, kan du udnytte en matrix og en simpel sløjfe for at forenkle koden. Dette er en yderligere fordel ved at designe kode for at foretrække grænseflader frem for klasser.

Advarsel!!!!

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