Novinci v jeziku Java so pogosto zmedeni. Njihova zmedenost je v veliki meri posledica palete eksotičnih jezikovnih lastnosti, kot so generiki in lambda. Vendar pa lahko celo preprostejše funkcije, kot so vmesniki, zmedejo.
Pred kratkim sem se soočil z vprašanjem, zakaj Java podpira vmesnike (prek vmesnik
in izvaja
ključne besede). Ko sem se v devetdesetih letih začel učiti Javo, so na to vprašanje pogosto odgovorili z izjavo, da vmesniki obvladujejo pomanjkanje podpore Java za dedovanje večkratne izvedbe (podrejeni razredi, ki dedujejo iz več nadrejenih razredov). Vendar vmesniki služijo veliko več kot le gonilo. V tem prispevku predstavljam šest vlog, ki jih imajo vmesniki v jeziku Java.
O večkratnem dedovanju
Izraz večkratno dedovanje se pogosto uporablja za sklicevanje na podrejeni razred, ki podeduje več roditeljskih razredov. V Javi izraz dedovanje večkratne izvedbe pomeni isto stvar. Java podpira tudi dedovanje več vmesnikov v katerem lahko podrejeni vmesnik podeduje iz več nadrejenih vmesnikov. Če želite izvedeti več o večkratnem dedovanju (vključno s slavnim problemom diamantov), si oglejte vnos Wikipedijevega več dedovanja.
Vloga 1: Navajanje vrst pripisov
The vmesnik
ključna beseda je preobremenjena za uporabo pri prijavljanju vrst pripisov. Na primer, seznam 1 predstavlja preprosto Stub
vrsta pripisa.
Seznam 1. Stub.java
import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Retention (RetentionPolicy.RUNTIME) public @interface Stub {int id (); // Podpičje zaključi izjavo elementa. String dueDate (); String razvijalec () privzeto "nedodeljen"; }
Stub
opisuje kategorijo opombe (primerki tipa pripisov), ki označujejo nedokončane vrste in metode. Njegova izjava se začne z glavo, sestavljeno iz @
čemur sledi vmesnik
ključni besedi, ki ji sledi ime.
Ta vrsta pripisa označuje tri elementi, ki si ga lahko omislite kot glavo metode:
id ()
vrne identifikator, ki temelji na celoštevilskem številurok()
določa datum, do katerega je treba škrbino izpolniti s kodorazvijalec ()
identificira razvijalca, odgovornega za polnjenje škrbine
Element vrne kakršno koli vrednost, ki mu jo dodeli pripis. Če element ni določen, je njegova privzeta vrednost (po privzeto
vrnjena ključna beseda v izjavi).
Seznam 2 prikazuje Stub
v kontekstu nedokončanega Kontakt
razred; razred in njegova samotna metoda so označeni z @Stub
opombe.
Seznam 2. ContactMgr.java
@Stub (id = 1, dueDate = "31.12.2016") javni razred ContactMgr {@Stub (id = 2, dueDate = "31.06.2016", razvijalec = "Marty") public void addContact (String contactID ) {}}
Primerek tipa pripisa se začne z @
, ki mu sledi ime vrste pripisa. Tukaj, prvi @Stub
Pripis se označi kot številka 1 z datumom zapadlosti 31. decembra 2016. Razvijalec, odgovoren za polnjenje škrbine, še ni dodeljen. Nasprotno pa drugi @Stub
Pripis se označi kot številka 2 z datumom zapadlosti 31. junija 2016. Razvijalec, odgovoren za polnjenje škrbine, je označen kot Marty.
Opombe je treba obdelati, da bodo koristne. (Stub
je označeno @Retention (RetentionPolicy.RUNTIME)
tako da ga je mogoče obdelati.) Na seznamu 3 so predstavljeni a StubFinder
aplikacija, ki poroča o razredu @Stub
opombe.
Seznam 3. StubFinder.java
uvoz java.lang.reflect.Method; javni razred StubFinder {public static void main (String [] args) vrže izjemo {if (args.length! = 1) {System.err.println ("uporaba: java StubFinder classfile"); vrnitev; } Class clazz = Class.forName (args [0]); if (clazz.isAnnotationPresent (Stub.class)) {Stub stub = clazz.getAnnotation (Stub.class); System.out.println ("Stub ID =" + stub.id ()); System.out.println ("Stub Date =" + stub.dueDate ()); System.out.println ("Stub Developer =" + stub.developer ()); System.out.println (); } Metoda [] metode = clazz.getMethods (); for (int i = 0; i <methods.length; i ++) if (methods [i] .isAnnotationPresent (Stub.class)) {Stub stub = methods [i] .getAnnotation (Stub.class); System.out.println ("Stub ID =" + stub.id ()); System.out.println ("Stub Date =" + stub.dueDate ()); System.out.println ("Stub Developer =" + stub.developer ()); System.out.println (); }}}
Seznam 3 glavni ()
metoda uporablja Java-ov Reflection API za pridobivanje vseh @Stub
pripise, ki predpišejo deklaracijo razreda in njene deklaracije metod.
Sestavi seznam 1 do 3, kot sledi:
javac * .java
Zaženite nastalo aplikacijo, kot sledi:
java StubFinder KontaktMgr
Upoštevati morate naslednje rezultate:
Stub ID = 1 Stub Date = 31.12.2016 Razvijalec Stub = nerazporejeni ID Stub = 2 Stub Date = 31.06.2016 Razvijalec Stub = Marty
Lahko trdite, da vrste pripisov in njihovi pripisi nimajo nič skupnega z vmesniki. Konec koncev, izjave razredov in izvaja
ključna beseda ni prisotna. Vendar se s tem sklepom ne bi strinjal.
@interface
je podoben razred
v tem, da uvaja tip. Njeni elementi so metode, ki se izvajajo (v ozadju) za vrnitev vrednosti. Elementi z privzeto
vrednosti vrnejo vrednosti, tudi če niso v pripisih, ki so podobni predmetom. V pripisu morajo biti vedno prisotni elementi, ki niso privzeti in jih je treba prijaviti, da vrnejo vrednost. Zato je kot da je bil razred razglašen in da razred izvaja metode vmesnika.
Vloga 2: Opis zmogljivosti, neodvisnih od izvedbe
Različni razredi lahko nudijo skupno sposobnost. Na primer java.nio.CharBuffer
, javax.swing.text.Segment
, java.lang.String
, java.lang.StringBuffer
, in java.lang.StringBuilder
razredi omogočajo dostop do berljivih zaporedij char
vrednote.
Kadar razredi ponujajo skupno sposobnost, lahko vmesnik te sposobnosti izvlečemo za ponovno uporabo. Na primer vmesnik do "berljivega zaporedja char
vrednosti "je bila izvlečena v java.lang.CharSequence
vmesnik. CharSequence
omogoča enoten, samo za branje dostop do številnih vrst char
zaporedja.
Recimo, da ste bili pozvani, da napišete majhno aplikacijo, ki šteje število pojavitev posamezne male črke v CharBuffer
, Vrvica
, in StringBuffer
predmetov. Po premisleku boste morda prišli do seznama 4. (Običajno bi se izogibal kulturno pristranskim izrazom, kot je ch - "a"
, vendar želim, da je primer preprost.)
Seznam 4. Freq.java
(različica 1)
uvoz java.nio.CharBuffer; javni razred Freq {javna statična void main (String [] args) {if (args.length! = 1) {System.err.println ("uporaba: besedilo java Freq"); vrnitev; } analyS (argumenti [0]); analySB (nov StringBuffer (args [0])); analizaCB (CharBuffer.wrap (args [0])); } statična analiza prazninCB (CharBuffer cb) {štetje int [] = novo int [26]; while (cb.hasRemaining ()) {char ch = cb.get (); če (ch> = 'a' && ch <= 'z') šteje [ch - 'a'] ++; } for (int i = 0; i <counts.length; i ++) System.out.printf ("Število% c je% d% n", (i + 'a'), šteje [i]); System.out.println (); } static void analyS (String s) {int counts [] = new int [26]; za (int i = 0; i = 'a' && ch <= 'z') šteje [ch - 'a'] ++; } for (int i = 0; i <counts.length; i ++) System.out.printf ("Število% c je% d% n", (i + 'a'), šteje [i]); System.out.println (); } statična praznina analySB (StringBuffer sb) {int counts [] = new int [26]; za (int i = 0; i = 'a' && ch <= 'z') šteje [ch - 'a'] ++; } for (int i = 0; i <counts.length; i ++) System.out.printf ("Število% c je% d% n", (i + 'a'), šteje [i]); System.out.println (); }}
Seznam 4 predstavlja tri različne analizirati
metode za beleženje števila pojavitev malih črk in izpis te statistike. Čeprav je Vrvica
in StringBuffer
različice so praktično enake (in morda vas bo zamikalo ustvariti eno samo metodo za obe), CharBuffer
varianta se bistveno razlikuje.
Seznam 4 razkriva veliko podvojenih kod, kar vodi do večje datoteke predavanj, kot je potrebno. Isti statistični cilj bi lahko dosegli z delom z CharSequence
vmesnik. Seznam 5 predstavlja nadomestno različico frekvenčne aplikacije, na kateri temelji CharSequence
.
Seznam 5. Freq.java
(različica 2)
uvoz java.nio.CharBuffer; javni razred Freq {javna statična void main (String [] args) {if (args.length! = 1) {System.err.println ("uporaba: besedilo java Freq"); vrnitev; } analizirati (argumenti [0]); analizirati (nov StringBuffer (args [0])); analizirajte (CharBuffer.wrap (args [0])); } statična analiza praznin (CharSequence cs) {int counts [] = new int [26]; za (int i = 0; i = 'a' && ch <= 'z') šteje [ch - 'a'] ++; } for (int i = 0; i <counts.length; i ++) System.out.printf ("Število% c je% d% n", (i + 'a'), šteje [i]); System.out.println (); }}
Seznam 5 razkriva veliko preprostejšo aplikacijo, ki je posledica kodifikacije analizirati ()
prejeti a CharSequence
prepir. Ker vsak od Vrvica
, StringBuffer
, in CharBuffer
izvaja CharSequence
, je zakonito prenašati primere teh vrst analizirati ()
.
Še en primer
Izraz CharBuffer.wrap (argumenti [0])
je še en primer podajanja Vrvica
objekt parametra tipa CharSequence
.
Če povzamemo, druga vloga vmesnika je opisati zmogljivost, neodvisno od izvedbe. S kodiranjem vmesnika (kot je CharSequence
) namesto v razred (na primer Vrvica
, StringBuffer
, ali CharBuffer
), se izognete podvojenim kodam in ustvarite manjše datoteke datotek. V tem primeru sem dosegel zmanjšanje za več kot 50%.
Vloga 3: Olajšanje razvoja knjižnic
Java 8 nas je seznanil z izjemno uporabno funkcijo lambda jezika in API-jem Streams (s poudarkom na tem, kakšno računanje je treba izvesti, ne pa na to, kako naj bo izvedeno). Lambde in potoki razvijalcem veliko olajšajo uvajanje vzporednosti v svoje aplikacije. Na žalost ogrodje zbirk Java ni mogel izkoristiti teh zmožnosti, ne da bi potreboval obsežno prepisovanje.
Če želite hitro izboljšati zbirke za uporabo kot viri toka in cilji, podpora za privzete metode (poznan tudi kot metode podaljšanja), ki so nestatične metode, katerih glave imajo predpono privzeto
ključna beseda in ki oskrbujejo kode telesa, je bila dodana vmesniku Java. Privzete metode pripadajo vmesnikom; jih ne izvajajo (lahko pa jih preglasijo) razredi, ki izvajajo vmesnike. Prav tako jih je mogoče priklicati prek referenc objekta.
Ko so privzete metode postale del jezika, so bile naslednje metode dodane v java.util.Collection
vmesnik, ki zagotavlja most med zbirkami in tokovi:
privzet tok Stream parallelStream ()
: Vrni (morda) vzporednikjava.util.stream.Stream
predmet s to zbirko kot izvorom.privzeti tok pretoka ()
: Vrni zaporedjeTok
predmet s to zbirko kot izvorom.
Recimo, da ste izjavili naslednje java.util.List
izraz spremenljivke in dodelitve:
Seznam innerPlanets = Arrays.asList ("Merkur", "Venera", "Zemlja", "Mars");
Tradicionalno bi ponavljali to zbirko, kot sledi:
za (String innerPlanet: innerPlanets) System.out.println (innerPlanet);
To zunanjo ponovitev, ki se osredotoča na to, kako izvesti izračun, lahko nadomestite z notranjo ponovitvijo, ki temelji na toku, ki se osredotoča na to, kateri izračun naj izvede, kot sledi:
innerPlanets.stream (). forEach (System.out :: println); innerPlanets.parallelStream (). forEach (System.out :: println);
Tukaj, innerPlanets.stream ()
in innerPlanets.parallelStream ()
vrni zaporedni in vzporedni tok predhodno ustvarjenemu Seznam
vir. Priklenjen na vrnjene Tok
reference je forEach (System.out :: println)
, ki se ponovi nad predmeti toka in prikliče System.out.println ()
(opredeljeno s strani System.out :: println
sklic na metodo) za vsak objekt, da prikaže svojo nizno predstavitev v standardni izhodni tok.
Privzete metode lahko naredijo kodo bolj berljivo. Na primer java.util.Collections
razred razglasi a void sort (seznam seznamov, primerjalnik c)
statična metoda za razvrščanje vsebine seznama, ki je predmet določene primerjalne enote. Java 8 je dodal a privzeta void sort (Primerjalnik c)
metoda do Seznam
vmesnik, tako da lahko pišete bolj berljivo myList.sort (primerjalnik);
namesto Collections.sort (myList, primerjalnik);
.
Privzeta vloga metode, ki jo ponujajo vmesniki, je dala novo življenje okolju Java Collections Framework. To vlogo lahko razmislite za lastne knjižnice, ki temeljijo na starejših vmesnikih.