Programování

Začněte s odkazy na metody v Javě

Spolu s lambdas přinesla Java SE 8 odkazy na metody v jazyce Java. Tento výukový program nabízí stručný přehled odkazů na metody v Javě a poté je můžete začít používat s příklady kódu Java. Na konci tohoto kurzu budete vědět, jak používat odkazy na metody k odkazování na statické metody třídy, vázané a nevázané nestatické metody a konstruktory, a také jak je používat k odkazování na metody instance v nadtřídě a aktuální třídě typy. Pochopíte také, proč mnoho vývojářů Java přijalo výrazy lambda a odkazy na metody jako čistší a jednodušší alternativu k anonymním třídám.

Všimněte si, že příklady kódu v tomto kurzu jsou kompatibilní s JDK 12.

stáhnout Získat kód Stáhněte si zdrojový kód například pro aplikace v tomto výukovém programu. Vytvořil Jeff Friesen pro JavaWorld.

Odkazy na metody: Základní nátěr

Můj předchozí výukový program Java 101 představil výrazy lambda, které se používají k definování anonymních metod, s nimiž lze potom zacházet jako s instancemi funkčního rozhraní. Výraz lambda někdy neudělá nic jiného než volání existující metody. Například následující fragment kódu používá k vyvolání lambda System.outje neplatný tisk (y) metoda na jediném argumentu lambda -styp zatím není znám:

(s) -> System.out.println (s)

Lambda představuje (s) jako jeho formální seznam parametrů a tělo kódu, jehož System.out.println (s) tisk výrazů shodnota pro standardní výstupní proud. Nemá explicitní typ rozhraní. Místo toho kompilátor vyvozuje z okolního kontextu, které funkční rozhraní má vytvořit instanci. Zvažte například následující fragment kódu:

Spotřebitel spotřebitel = (s) -> System.out.println (s);

Kompilátor analyzuje předchozí deklaraci a určuje, že java.util.function.Consumer předdefinované funkční rozhraní neplatnost přijmout (T t) metoda odpovídá formálnímu seznamu parametrů lambda ((s)). To také určuje akceptovat()je prázdnota návratový typ odpovídá println ()je prázdnota návratový typ. Lambda je tedy vázaný na Spotřebitel.

Přesněji řečeno, lambda je vázána na Spotřebitel. Kompilátor generuje kód tak, že vyvolá Spotřebitelje void přijmout (řetězce) metoda má za následek předání řetězcového argumentu s předán System.outje void println (String s) metoda. Toto vyvolání je uvedeno níže:

consumer.accept ("Hello"); // Předejte „Hello“ tělu lambda. Tisk Hello na standardní výstup.

Chcete-li uložit stisknutí kláves, můžete lambdu nahradit a odkaz na metodu, což je kompaktní odkaz na existující metodu. Například nahradí následující fragment kódu (String s) -> System.out.println (s) s System.out :: println, kde :: znamená to System.outje void println (String s) metoda je odkazována:

Spotřebitel consumer2 = System.out :: println; // Odkaz na metodu je kratší. consumer2.accept ("Hello"); // Předejte „Hello“ tělu lambda. Tisk Hello na standardní výstup.

Není nutné specifikovat formální seznam parametrů pro předchozí odkaz na metodu, protože kompilátor může tento seznam odvodit na základě Spotřebitel Tento parametrovaný typ řetězec java.lang skutečný typ argumentu nahrazuje T v neplatnost přijmout (T t), a je také typem jediného parametru v těle lambda System.out.println () volání metody.

Metodické odkazy do hloubky

A odkaz na metodu je syntaktická zkratka pro vytvoření lambda z existující metody. Místo poskytnutí těla implementace odkaz na metodu odkazuje na metodu existující třídy nebo objektu. Stejně jako u lambda vyžaduje odkaz na metodu cílový typ.

Odkazy na metody můžete použít k odkázání na statické metody třídy, vázané a nevázané nestatické metody a konstruktory. Můžete také použít odkazy na metody k odkazování na metody instance v nadtřídě a aktuálních typech tříd. Představím vám každou z těchto referenčních kategorií metod a ukážu, jak se používají v malé ukázce.

Další informace o odkazech na metody

Po přečtení této části se podívejte na Odkazy na metody v Javě 8 (Toby Weston, únor 2014), kde získáte další informace o odkazech na metody ve vázaných a nevázaných nestatických kontextech metod.

Odkazy na statické metody

A statická reference metody odkazuje na statickou metodu v konkrétní třídě. Jeho syntaxe je jméno třídy::staticMethodName, kde jméno třídy identifikuje třídu a staticMethodName identifikuje statickou metodu. Příkladem je Celé číslo :: bitCount. Výpis 1 ukazuje odkaz na statickou metodu.

Výpis 1. MRDemo.java (verze 1)

import java.util.Arrays; import java.util.function.Consumer; veřejná třída MRDemo {public static void main (String [] args) {int [] pole = {10, 2, 19, 5, 17}; Spotřebitel spotřebitel = Pole :: řazení; consumer.accept (pole); for (int i = 0; i <array.length; i ++) System.out.println (pole [i]); System.out.println (); int [] pole2 = {19, 5, 14, 3, 21, 4}; Spotřebitel consumer2 = (a) -> Arrays.sort (a); consumer2.accept (array2); for (int i = 0; i <array2.length; i ++) System.out.println (pole2 [i]); }}

Seznam 1 hlavní() metoda seřadí dvojici celočíselných polí přes java.util.Arrays třídy static void sort (int [] a) metoda, která se objevuje ve statických referencích metod a ekvivalentních kontextech výrazů lambda. Po seřazení pole a pro smyčka vytiskne obsah seřazeného pole do standardního výstupního proudu.

Než budeme moci použít odkaz na metodu nebo lambdu, musí být vázán na funkční rozhraní. Používám předdefinované Spotřebitel funkční rozhraní, které splňuje požadavky metody reference / lambda. Operace třídění začíná předáním pole, do kterého se má řadit Spotřebitelje akceptovat() metoda.

Zkompilovat výpis 1 (javac MRDemo.java) a spusťte aplikaci (java MRDemo). Budete sledovat následující výstup:

2 5 10 17 19 3 4 5 14 19 21

Odkazy na vázané nestatické metody

A vázaný odkaz na nestatickou metodu odkazuje na nestatickou metodu, která je vázána na a přijímač objekt. Jeho syntaxe je název_objektu::instanceMethodName, kde název_objektu identifikuje příjemce a instanceMethodName identifikuje metodu instance. Příkladem je s :: trim. Výpis 2 ukazuje vázaný odkaz na nestatickou metodu.

Výpis 2. MRDemo.java (verze 2)

import java.util.function.Supplier; veřejná třída MRDemo {public static void main (String [] args) {String s = "Rychlá hnědá liška přeskočila na líného psa"; tisk (s :: délka); print (() -> s.length ()); print (new Supplier () {@Override public Integer get () {return s.length (); // zavírá přes s}}); } public static void print (dodavatel dodavatele) {System.out.println (supplier.get ()); }}

Výpis 2 hlavní() metoda přiřadí řetězec Tětiva proměnná s a poté vyvolá tisk() metoda třídy s funkčností k získání délky tohoto řetězce jako argumentu této metody. tisk() je vyvolána v odkazu na metodu (s :: délka -- délka() je vázán s), ekvivalentní lambda a ekvivalentní kontexty anonymních tříd.

Definoval jsem tisk() používat java.util.function.Supplier předdefinované funkční rozhraní, jehož dostat() metoda vrací dodavatele výsledků. V tomto případě Dodavatel instance předána tisk() implementuje své dostat() způsob návratu s.length (); tisk() výstupy této délky.

s :: délka zavádí uzávěr, který se uzavírá s. Vidíte to jasněji v příkladu lambda. Protože lambda nemá žádné argumenty, hodnota s je k dispozici pouze z přiloženého rozsahu. Proto je lambda tělo uzávěrem, který se zavírá s. Příklad anonymní třídy to činí ještě jasnějším.

Zkompilujte výpis 2 a spusťte aplikaci. Budete sledovat následující výstup:

44 44 44

Odkazy na nevázané nestatické metody

An nevázaný odkaz na nestatickou metodu odkazuje na nestatickou metodu, která není vázána na objekt přijímače. Jeho syntaxe je jméno třídy::instanceMethodName, kde jméno třídy identifikuje třídu, která deklaruje metodu instance a instanceMethodName identifikuje metodu instance. Příkladem je String :: toLowerCase.

String :: toLowerCase je nevázaná nestatická reference na metodu, která identifikuje nestatickou Řetězec toLowerCase () metoda Tětiva třída. Protože však statická metoda stále vyžaduje objekt přijímače (v tomto příkladu a Tětiva objekt, který se používá k vyvolání toLowerCase () prostřednictvím odkazu na metodu) je objekt přijímače vytvořen virtuálním strojem. toLowerCase () bude vyvolán u tohoto objektu. String :: toLowerCase určuje metodu, která trvá jeden Tětiva argument, který je objektem příjemce, a vrací a Tětiva výsledek. String :: toLowerCase () je ekvivalentní lambda (String s) -> {return s.toLowerCase (); }.

Výpis 3 ukazuje tento nevázaný odkaz na nestatickou metodu.

Výpis 3. MRDemo.java (verze 3)

import java.util.function.Function; public class MRDemo {public static void main (String [] args) {print (String :: toLowerCase, "STRING TO LOWERCASE"); print (s -> s.toLowerCase (), "STRING TO LOWERCASE"); print (new Function () {@Override public String apply (String s) // přijímá argument v parametru s; {// nemusí uzavírat přes s return s.toLowerCase ();}}, „STRING TO LOWERCASE“ ); } public static void print (Function function, String s) {System.out.println (function.apply (s)); }}

Výpis 3 hlavní() metoda vyvolá tisk() metoda třídy s funkcemi pro převod řetězce na malá písmena a řetězec, který má být převeden jako argumenty metody. tisk() je vyvolána v odkazu na metodu (String :: toLowerCase, kde toLowerCase () není vázán na objekt zadaný uživatelem) a ekvivalentní kontexty lambda a anonymní třídy.

Definoval jsem tisk() používat java.util.function.Function předdefinované funkční rozhraní, které představuje funkci, která přijímá jeden argument a vytváří výsledek. V tomto případě Funkce instance předána tisk() implementuje své R platí (T t) způsob návratu s.toLowerCase (); tisk() vypíše tento řetězec.

Ačkoliv Tětiva část String :: toLowerCase vypadá, že se odkazuje na třídu, odkazuje se pouze na instanci této třídy. Díky příkladu anonymní třídy je to jasnější. Všimněte si, že v příkladu anonymní třídy lambda obdrží argument; nezavírá se nad parametrem s (tj. nejde o uzávěr).

Zkompilujte výpis 3 a spusťte aplikaci. Budete sledovat následující výstup:

řetězec na malá písmena řetězec na malá písmena řetězec na malá písmena

Odkazy na konstruktory

Odkaz na metodu můžete použít k odkázání na konstruktor bez vytvoření instance pojmenované třídy. Tento druh odkazu na metodu je znám jako a odkaz na konstruktor. Jeho syntaxe je jméno třídy::Nový. jméno třídy musí podporovat vytváření objektů; nemůže pojmenovat abstraktní třídu nebo rozhraní. Klíčové slovo Nový pojmenuje odkazovaný konstruktor. Zde jsou nějaké příklady:

  • Znak :: nový: ekvivalentní lambda (Znak ch) -> nový Znak (ch)
  • Dlouhé :: nové: ekvivalentní lambda (dlouhá hodnota) -> nová dlouhá (hodnota) nebo (String s) -> new Long (s)
  • ArrayList :: nový: ekvivalentní lambda () -> new ArrayList ()
  • float [] :: nové: ekvivalentní lambda (int size) -> new float [size]

Poslední referenční příklad konstruktoru určuje typ pole místo typu třídy, ale princip je stejný. Příklad ukazuje odkaz na konstruktor pole na „konstruktor“ typu pole.

Chcete-li vytvořit odkaz na konstruktor, zadejte Nový bez konstruktoru. Když třída jako java.lang.Long deklaruje více konstruktorů, kompilátor porovná typ funkčního rozhraní se všemi konstruktory a vybere nejlepší shodu. Výpis 4 ukazuje odkaz na konstruktor.

Výpis 4. MRDemo.java (verze 4)

import java.util.function.Supplier; veřejná třída MRDemo {public static void main (String [] args) {dodavatel dodavatele = MRDemo :: nový; System.out.println (supplier.get ()); }}

Výpis 4 MRDemo :: nové odkaz na konstruktor je ekvivalentní lambda () -> nový MRDemo (). Výraz supply.get () provede tuto lambdu, která vyvolá MRDemoje výchozí konstruktor bez argumentů a vrátí MRDemo objekt, který je předán System.out.println (). Tato metoda převede objekt na řetězec, který vytiskne.

Nyní předpokládejme, že máte třídu s konstruktorem bez argumentu a konstruktorem, který přebírá argument, a chcete zavolat konstruktoru, který přebírá argument. Tuto úlohu můžete provést výběrem jiného funkčního rozhraní, například předdefinovaného Funkce rozhraní zobrazené v seznamu 5.

Výpis 5. MRDemo.java (verze 5)

import java.util.function.Function; veřejná třída MRDemo {soukromé jméno řetězce; MRDemo () {name = ""; } MRDemo (název řetězce) {this.name = name; System.out.printf ("MRDemo (název řetězce) s názvem% s% n", jméno); } public static void main (String [] args) {Function function = MRDemo :: new; System.out.println (function.apply ("nějaké jméno")); }}

Funkce funkce = MRDemo :: new; způsobí, že kompilátor hledá konstruktor, který přebírá a Tětiva argument, protože Funkceje aplikovat() metoda vyžaduje jeden (v tomto kontextu) Tětiva argument. Provádění function.apply ("nějaké jméno") výsledky v "nějaké jméno" předán MRDemo (název řetězce).