Programovanie

Šesť rolí rozhrania

Nováčikovia v jazyku Java často pociťujú zmätok. Ich zmätok je do veľkej miery spôsobený paletou exotických jazykových prvkov Java, ako sú generiká a lambdy. Avšak aj jednoduchšie funkcie, ako sú rozhrania, môžu byť zarážajúce.

Nedávno som narazil na otázku, prečo Java podporuje rozhrania (cez rozhranie a náradie Kľúčové slová). Keď som sa v 90. rokoch začal učiť Javu, na túto otázku som často odpovedal tvrdením, že rozhrania obchádzajú nedostatočnú podporu Javy pre viacnásobné dedičstvo implementácie (podradené triedy dediace z viacerých rodičovských tried). Rozhrania však slúžia oveľa viac ako záplava. V tomto príspevku predstavujem šesť rolí, ktoré rozhrania hrajú v jazyku Java.

O viacnásobnom dedičstve

Termín viacnásobné dedičstvo sa bežne používa na označenie podradenej triedy dediacej z viacerých nadradených tried. V Jave výraz viacnásobné dedičstvo implementácie znamená to isté. Java tiež podporuje dedičnosť viacerých rozhraní v ktorom môže podradené rozhranie dediť z viacerých nadradených rozhraní. Ak sa chcete dozvedieť viac o viacnásobnom dedičstve (vrátane slávneho problému s diamantmi), pozrite si záznam Viacnásobné dedičstvo na Wikipédii.

Úloha 1: Deklarovanie typov anotácií

The rozhranie kľúčové slovo je preťažené na použitie pri deklarovaní typov anotácií. Napríklad zoznam 1 predstavuje jednoduchý Pahýľ typ anotácie.

Zoznam 1. Stub.java

import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Retention (RetentionPolicy.RUNTIME) public @interface Stub {int id (); // Bodkočiarka ukončuje deklaráciu prvku. Reťazec dueDate (); Reťazcový vývojár () predvolené "nepriradené"; }

Pahýľ popisuje kategóriu anotácie (inštancie typu anotácie), ktoré označujú nedokončené typy a metódy. Jeho vyhlásenie sa začína hlavičkou, ktorá sa skladá z @ nasledované rozhranie kľúčové slovo, za ktorým nasleduje jeho názov.

Tento typ anotácie deklaruje tri prvkov, ktoré si môžete predstaviť ako hlavičky metód:

  • id () vráti celočíselný identifikátor útržku
  • do dátumu() označuje dátum, do ktorého musí byť pahýl vyplnený kódom
  • vývojár () identifikuje vývojára zodpovedného za vyplnenie stubu

Element vracia akúkoľvek hodnotu, ktorá mu je priradená anotáciou. Ak prvok nie je zadaný, jeho predvolená hodnota (po predvolené kľúčové slovo vo vyhlásení).

Zoznam 2 ukazuje Pahýľ v kontexte nedokončenej KontaktMgr trieda; trieda a jej samotárska metóda boli anotované @Stub anotácie.

Zoznam 2. ContactMgr.java

@Stub (id = 1, dueDate = "31.12.2016") verejná trieda ContactMgr {@Stub (id = 2, dueDate = "31.06.2016", developer = "Marty") public void addContact (reťazec contactID ) {}}

Inštancia typu anotácie začína @, za ktorým nasleduje názov typu anotácie. Tu prvý @Stub anotácia sa označuje ako číslo 1 s dátumom splatnosti 31. decembra 2016. Vývojár zodpovedný za vyplnenie stubu zatiaľ nebol pridelený. Oproti tomu druhá @Stub anotácia sa označuje ako číslo 2 s dátumom splatnosti 31. júna 2016. Vývojár zodpovedný za vyplnenie útržku je identifikovaný ako Marty.

Anotácie musia byť spracované, aby boli použiteľné. (Pahýľ je anotovaný @Retention (RetentionPolicy.RUNTIME) aby sa dalo spracovať.) Výpis 3 predstavuje a StubFinder aplikácia, ktorá hlási triedu @Stub anotácie.

Zoznam 3. StubFinder.java

import java.lang.reflect.Method; public class StubFinder {public static void main (String [] args) throws Exception {if (args.length! = 1) {System.err.println ("usage: java StubFinder classfile"); návrat; } Class clazz = Class.forName (args [0]); if (clazz.isAnnotationPresent (Stub.class)) {Stub stub = clazz.getAnnotation (Stub.class); System.out.println ("ID stubu =" + stub.id ()); System.out.println ("Stub Date =" + stub.dueDate ()); System.out.println ("Stub Developer =" + stub.developer ()); System.out.println (); } Metóda [] methods = 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 ("ID stubu =" + stub.id ()); System.out.println ("Stub Date =" + stub.dueDate ()); System.out.println ("Stub Developer =" + stub.developer ()); System.out.println (); }}}

Zoznam 3 hlavný() metóda používa Java Reflection API na získanie všetkých @Stub anotácie, ktoré predponujú deklaráciu triedy, ako aj deklarácie jej metód.

Zostavte zoznamy 1 až 3 takto:

javac * .java

Výslednú aplikáciu spustite takto:

java StubFinder ContactMgr

Mali by ste dodržiavať nasledujúci výstup:

ID zákazníka = 1 Dátum zákazu = 31.12.2016 Vývojár zákazky = nezadané ID zákazníka = 2 Dátum zákazu = 31.06.2016 Vývojár zákazky = Marty

Môžete namietať, že typy anotácií a ich anotácie nemajú nič spoločné s rozhraniami. Koniec koncov, deklarácie tried a náradie kľúčové slovo nie je k dispozícii. S týmto záverom by som však nesúhlasil.

@rozhranie je podobný trieda v tom, že zavádza typ. Jeho prvky sú metódy, ktoré sú implementované (v zákulisí) na vrátenie hodnôt. Prvky s predvolené hodnoty vracajú hodnoty, aj keď nie sú prítomné v anotáciách, ktoré sú podobné objektom. Nepredvolené prvky musia byť vždy obsiahnuté v anotácii a musia byť deklarované, aby vrátili hodnotu. Preto je to, akoby bola deklarovaná trieda a že trieda implementuje metódy rozhrania.

Úloha 2: Popis schopností nezávislých od implementácie

Rôzne triedy môžu ponúkať spoločné možnosti. Napríklad java.nio.CharBuffer, javax.swing.text.Segment, java.lang.String, java.lang.StringBuffera java.lang.StringBuilder triedy poskytujú prístup k čitateľným sekvenciám char hodnoty.

Keď triedy ponúkajú spoločné funkcie, rozhranie pre túto funkciu je možné extrahovať na ďalšie použitie. Napríklad rozhranie k „čitateľnej postupnosti súboru char Hodnoty "boli extrahované do java.lang.CharSequence rozhranie. CharSequence poskytuje jednotný prístup iba na čítanie k mnohým rôznym druhom súborov char sekvencie.

Predpokladajme, že ste boli požiadaní, aby ste napísali malú aplikáciu, ktorá počíta počet výskytov každého druhu malých písmen CharBuffer, Stringa StringBuffer predmety. Po dlhšom premýšľaní by ste mohli prísť s Výpisom 4. (Ja by som sa obvykle vyhýbal kultúrne zaujatým výrazom ako napr ch - „a“, ale chcem príklad ponechať jednoduchý.)

Výpis 4. Freq.java (verzia 1)

import java.nio.CharBuffer; public class Freq {public static void main (String [] args) {if (args.length! = 1) {System.err.println ("usage: java Freq text"); návrat; } analyseS (args [0]); analyzeSB (nový StringBuffer (args [0])); analyzeCB (CharBuffer.wrap (args [0])); } static void analyzeCB (CharBuffer cb) {int count [] = new int [26]; while (cb.hasRemaining ()) {char ch = cb.get (); if (ch> = 'a' && ch <= 'z') počíta [ch - 'a'] ++; } for (int i = 0; i <countts.length; i ++) System.out.printf ("Počet% c je% d% n", (i + 'a'), count [i]); System.out.println (); } static void analyzeS (String s) {int count [] = new int [26]; pre (int i = 0; i = 'a' && ch <= 'z') počíta [ch - 'a'] ++; } for (int i = 0; i <countts.length; i ++) System.out.printf ("Počet% c je% d% n", (i + 'a'), count [i]); System.out.println (); } static void analyzeSB (StringBuffer sb) {int count [] = new int [26]; pre (int i = 0; i = 'a' && ch <= 'z') počíta [ch - 'a'] ++; } for (int i = 0; i <countts.length; i ++) System.out.printf ("Počet% c je% d% n", (i + 'a'), count [i]); System.out.println (); }}

Výpis 4 predstavuje tri rôzne analyzovať metódy na zaznamenávanie počtu výskytov malých písmen a na výstup tejto štatistiky. Napriek tomu String a StringBuffer varianty sú prakticky identické (a mohlo by vás lákať vytvoriť jednu metódu pre obidve), CharBuffer variant sa výraznejšie líši.

Zoznam 4 odhaľuje veľa duplicitných kódov, čo vedie k väčšiemu triednemu súboru, ako je potrebné. Rovnaký štatistický cieľ by ste mohli dosiahnuť spoluprácou s CharSequence rozhranie. Zoznam 5 predstavuje alternatívnu verziu frekvenčnej aplikácie, na ktorej je založená CharSequence.

Zoznam 5. Freq.java (verzia 2)

import java.nio.CharBuffer; public class Freq {public static void main (String [] args) {if (args.length! = 1) {System.err.println ("usage: java Freq text"); návrat; } analyzovať (args [0]); analyzovať (nový StringBuffer (args [0])); analyzovať (CharBuffer.wrap (args [0])); } statická analýza prázdnoty (CharSequence cs) {int počíta [] = nový int [26]; pre (int i = 0; i = 'a' && ch <= 'z') počíta [ch - 'a'] ++; } for (int i = 0; i <countts.length; i ++) System.out.printf ("Počet% c je% d% n", (i + 'a'), count [i]); System.out.println (); }}

Zoznam 5 odhaľuje oveľa jednoduchšiu aplikáciu, ktorá je dôsledkom kodifikácie analyzovať () prijať a CharSequence argument. Pretože každý z String, StringBuffera CharBuffer náradie CharSequence, je legálne odovzdávať prípady týchto typov analyzovať ().

Ďalší príklad

Vyjadrenie CharBuffer.wrap (args [0]) je ďalší príklad zloženia a String namietať proti parametru typu CharSequence.

Stručne povedané, druhou úlohou rozhrania je popísať schopnosť nezávislú od implementácie. Kódovaním do rozhrania (napr CharSequence) namiesto do triedy (napríklad String, StringBufferalebo CharBuffer), vyhnete sa duplicitnému kódu a generujete menšie súbory tried. V tomto prípade som dosiahol zníženie o viac ako 50%.

Úloha 3: Uľahčenie vývoja knižnice

Java 8 nám predstavila mimoriadne užitočnú funkciu jazyka lambda a rozhranie Streams API (so zameraním skôr na to, aký výpočet by sa mal vykonať, ako na to, ako by sa mal vykonať). Aplikácia Lambdas and Streams výrazne uľahčuje vývojárom zavádzanie paralelizmu do ich aplikácií. Bohužiaľ, Java Collections Framework nemohol tieto schopnosti využiť bez toho, aby potreboval rozsiahle prepísanie.

Ak chcete rýchlo vylepšiť zbierky na použitie ako zdroje a ciele streamu, podpora pre predvolené metódy (taktiež známy ako rozširovacie metódy), čo sú nestatické metódy, ktorých hlavičky majú predponu predvolené kľúčové slovo a ktorý dodáva telá kódov kódu, bolo pridané do funkcie rozhrania Java. Predvolené metódy patria k rozhraniam; nie sú implementované (ale môžu byť prepísané) triedami, ktoré implementujú rozhrania. Dajú sa tiež vyvolať prostredníctvom odkazov na objekty.

Akonáhle sa predvolené metódy stali súčasťou jazyka, boli do jazyka pridané nasledujúce metódy java.util.Collection rozhranie, ktoré poskytuje most medzi zbierkami a prúdmi:

  • predvolený stream paralelný prúd (): Vrátiť (prípadne) paralelu java.util.stream.Stream objekt s touto kolekciou ako zdrojom.
  • predvolený prúd streamu (): Vrátiť postupnosť Prúd objekt s touto kolekciou ako zdrojom.

Predpokladajme, že ste deklarovali nasledujúce java.util.List premenná a priradenie výraz:

Zoznam innerPlanets = Arrays.asList ("Merkúr", "Venuša", "Zem", "Mars");

Túto kolekciu by ste tradične iterovali takto:

pre (String innerPlanet: innerPlanets) System.out.println (innerPlanet);

Túto externú iteráciu, ktorá sa zameriava na to, ako vykonať výpočet, môžete nahradiť internou iteráciou založenou na streamoch, ktorá sa zameriava na to, aký výpočet treba vykonať, a to nasledovne:

innerPlanets.stream (). forEach (System.out :: println); innerPlanets.parallelStream (). forEach (System.out :: println);

Tu, innerPlanets.stream () a innerPlanets.parallelStream () vráti sekvenčné a paralelné prúdy do predtým vytvorených Zoznam zdroj. Pripútaný k vrátenému Prúd referencie je forEach (System.out :: println), ktorý iteruje nad objektmi prúdu a vyvoláva ho System.out.println () (označené System.out :: println odkaz na metódu) pre každý objekt na výstup svojej reťazovej reprezentácie do štandardného výstupného toku.

Predvolené metódy umožňujú lepšiu čitateľnosť kódu. Napríklad java.util.Collections trieda vyhlasuje a void sort (zoznam, komparátor c) statická metóda na triedenie obsahu zoznamu, ktorý podlieha uvedenému komparátoru. Java 8 pridal a predvolené triedenie void (porovnávač c) metóda do Zoznam rozhranie, aby ste mohli písať čitateľnejšie myList.sort (komparátor); namiesto Collections.sort (myList, komparátor);.

Rola predvolenej metódy ponúkaná rozhraniami dala nový život rámcu kolekcií Java. Túto rolu môžete zvážiť pre svoje vlastné staršie knižnice založené na rozhraní.

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