Započnite s referencama metoda u Javi

Zajedno s lambdama, Java SE 8 donio je reference metoda na jezik Java. Ovaj vodič nudi kratki pregled referenci metoda u Javi, a zatim započinje s korištenjem primjera Java koda. Na kraju tutorijala znat ćete kako se referencama metoda pozivati ​​na statičke metode klase, vezane i nevezane ne-statičke metode i konstruktore, kao i kako ih koristiti za pozivanje na metode instance u superklasi i trenutnoj klasi vrste. Također ćete razumjeti zašto su mnogi programeri Java usvojili lambda izraze i reference metoda kao čistiju i jednostavniju alternativu anonimnim klasama.

Primijetite da su primjeri koda u ovom vodiču kompatibilni s JDK 12.

preuzimanje Preuzmite kod Preuzmite izvorni kod za primjere aplikacija u ovom vodiču. Stvorio Jeff Friesen za JavaWorld.

Reference metode: Primer

Moj prethodni udžbenik Java 101 predstavio je lambda izraze koji se koriste za definiranje anonimnih metoda koje se potom mogu tretirati kao primjerci funkcionalnog sučelja. Ponekad, lambda izraz ne čini ništa drugo nego poziva postojeću metodu. Na primjer, sljedeći kod fragment koristi lambda pozvati System.outje void println(s)način na lambda-a jednom argument-- stipa koji još nije poznat:

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

Lambda predstavlja (s)svoj formalni popis parametara i tijelo koda čiji System.out.println(s)izraz ispisuje svrijednost u standardni izlazni tok. Nema eksplicitnu vrstu sučelja. Umjesto toga, prevodilac zaključuje iz okolnog konteksta koje funkcionalno sučelje treba stvoriti. Na primjer, uzmite u obzir sljedeći fragment koda:

Consumer consumer = (s) -> System.out.println(s);

Prevoditelj analizira prethodnu deklaraciju i utvrđuje da metoda java.util.function.Consumerunaprijed definiranog funkcionalnog sučelja void accept(T t)odgovara lambda formalnom popisu parametara ( (s)). Također utvrđuje da se accept()' voidreturn type podudara println()s' voidreturn type. Lambda je tako vezana za Consumer.

Točnije, lambda je dužna Consumer. Kompajler generira kôd tako da pozivanje metode Consumer'' '' void accept(String s)rezultira argumentom niza koji sse prosljeđuje metodi System.out'' void println(String s). Ovaj je poziv prikazan u nastavku:

consumer.accept("Hello"); // Pass "Hello" to lambda body. Print Hello to standard output.

Da biste spremili pritiske tipki, lambdu možete zamijeniti referencom metode , koja je kompaktna referenca na postojeću metodu. Na primjer, sljedeći fragment koda zamjenjuje (String s) -> System.out.println(s)sa System.out::println, gdje ::označava referencu System.outna void println(String s)metodu:

Consumer consumer2 = System.out::println; // The method reference is shorter. consumer2.accept("Hello"); // Pass "Hello" to lambda body. Print Hello to standard output.

Nije potrebno navesti službeni popis parametara za prethodni postupak pozivanjem jer prevodilac može zaključiti taj popis na temelju Consumerovog parametriziranoj Tip je java.lang.Stringstvarni tip argumenata zamjenjuje Tu void accept(T t), te je također vrsta jedinstvenog parametra u lambda tijela System.out.println()metodom poziva.

Dubinske reference metoda

Metoda referenca je sintaktička prečac za stvaranje lambda iz postojeće metode. Umjesto pružanja tijela za implementaciju, referenca na metodu odnosi se na postojeću klasu ili metodu objekta. Kao i kod lambde, referenca metode zahtijeva ciljnu vrstu.

Pomoću referenci na metode možete se pozvati na statičke metode klase, vezane i nevezane ne-statičke metode i konstruktore. Također se možete koristiti referencama metoda za pozivanje na metode instance u superklasi i trenutnim vrstama klasa. Upoznat ću vas sa svakom od ovih referentnih kategorija metoda i pokazati kako se koriste u malom pokaznom prikazu.

Saznajte više o referencama metoda

Nakon čitanja ovog odjeljka, pogledajte Reference referenci u Javi 8 (Toby Weston, veljača 2014.) za dodatni uvid u reference metoda u vezanim i nevezanim ne-statičkim kontekstima metoda.

Upućivanje na statičke metode

Statička metoda referenca odnosi na postupak statički u određenoj klasi. Njegova sintaksa je , gdje identificira klasu i identificira statičku metodu. Primjer je . Popis 1 prikazuje referencu statičke metode.className::staticMethodNameclassNamestaticMethodNameInteger::bitCount

Popis 1. MRDemo.java (verzija 1)

import java.util.Arrays; import java.util.function.Consumer; public class MRDemo { public static void main(String[] args) { int[] array = { 10, 2, 19, 5, 17 }; Consumer consumer = Arrays::sort; consumer.accept(array); for (int i = 0; i < array.length; i++) System.out.println(array[i]); System.out.println(); int[] array2 = { 19, 5, 14, 3, 21, 4 }; Consumer consumer2 = (a) -> Arrays.sort(a); consumer2.accept(array2); for (int i = 0; i < array2.length; i++) System.out.println(array2[i]); } }

main()Metoda popisa 1 sortira par čitavih nizova putem metode java.util.Arraysklase static void sort(int[] a), koja se pojavljuje u referencama statičke metode i ekvivalentnim kontekstima lambda izraza. Nakon sortiranja niza, forpetlja ispisuje sadržaj sortiranog niza u standardni izlazni tok.

Prije nego što upotrijebimo referencu na metodu ili lambda, ona mora biti vezana za funkcionalno sučelje. Koristim unaprijed definirano Consumerfunkcionalno sučelje, koje udovoljava zahtjevima reference / lambda. Na vrsta rada počinje polaganjem niz biti riješeno do Consumer„s accept()metodom.

Sastavite popis 1 ( javac MRDemo.java) i pokrenite aplikaciju ( java MRDemo). Primijetit ćete sljedeći rezultat:

2 5 10 17 19 3 4 5 14 19 21

Upućivanje na vezane nestatičke metode

Vezani ne-statička metoda referenca se odnosi na ne-statička metoda a koja je vezana za prijemnik objekt. Njegova sintaksa je , gdje identificira primatelj i identificira metodu instance. Primjer je . Popis 2 prikazuje vezanu nestetičku referencu metode.objectName::instanceMethodNameobjectNameinstanceMethodNames::trim

Popis 2. MRDemo.java (verzija 2)

import java.util.function.Supplier; public class MRDemo { public static void main(String[] args) { String s = "The quick brown fox jumped over the lazy dog"; print(s::length); print(() -> s.length()); print(new Supplier() { @Override public Integer get() { return s.length(); // closes over s } }); } public static void print(Supplier supplier) { System.out.println(supplier.get()); } }

main()Metoda popisa 2 dodjeljuje niz Stringvarijabli, sa zatim poziva print()metodu klase s funkcionalnošću kako bi se dobila duljina ovog niza kao argument ove metode. print()poziva se u referenci metode ( s::length- length()vezana je za s), ekvivalentnoj lambda i ekvivalentnim kontekstima anonimne klase.

Definirao print()sam upotrebu java.util.function.Supplierunaprijed definiranog funkcionalnog sučelja čija get()metoda vraća dobavljača rezultata. U ovom slučaju, Supplierinstanca proslijeđena print()implementira svoju get()metodu za povratak s.length(); print()izlazi ova duljina.

s::lengthuvodi zatvaranje koje se zatvara s. To možete jasnije vidjeti na primjeru lambda. Budući da lambda nema argumente, vrijednost sje dostupna samo iz priloženog opsega. Stoga je lambda tijelo zatvarač koji se zatvara s. Anonimni primjer klase to čini još jasnijim.

Sastavite popis 2 i pokrenite aplikaciju. Primijetit ćete sljedeći rezultat:

44 44 44

Upućivanja na nevezane nestatičke metode

Nevezane ne-statička metoda referenca odnosi na ne-statička metoda za koji nije vezan na objekt prijemnika. Njegova sintaksa je , gdje identificira klasu koja deklarira metodu instance i identificira metodu instance. Primjer je .className::instanceMethodNameclassNameinstanceMethodNameString::toLowerCase

String::toLowerCaseje nevezana nestetička referenca metode koja identificira nestetičku String toLowerCase()metodu Stringklase. Međutim, budući da nestatična metoda i dalje zahtijeva objekt primatelja (u ovom primjeru Stringobjekt koji se koristi za pozivanje toLowerCase()putem reference metode), objekt primatelja stvara virtualni stroj. toLowerCase()će se pozivati ​​na ovom objektu. String::toLowerCasespecificira metodu koja uzima jedan Stringargument, koji je objekt primatelja, i vraća Stringrezultat. String::toLowerCase()je ekvivalentan lambda (String s) -> { return s.toLowerCase(); }.

Popis 3 pokazuje ovu nevezanu referencu nestetičke metode.

Popis 3. MRDemo.java (verzija 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) // receives argument in parameter s; { // doesn't need to close over s return s.toLowerCase(); } }, "STRING TO LOWERCASE"); } public static void print(Function function, String s) { System.out.println(function.apply(s)); } }

main()Metoda popisa 3 poziva metodu print()klase s funkcionalnošću za pretvaranje niza u mala slova i niz koji treba pretvoriti kao argumente metode. print()poziva se u referenci metode ( String::toLowerCase, gdje toLowerCase()nije vezan za objekt koji je odredio korisnik) i ekvivalentnom kontekstu lambda i anonimne klase.

Definirao print()sam upotrebu java.util.function.Functionunaprijed definiranog funkcionalnog sučelja, koje predstavlja funkciju koja prihvaća jedan argument i daje rezultat. U ovom slučaju, Functioninstanca proslijeđena print()implementira svoju R apply(T t)metodu za povratak s.toLowerCase(); print()izlazi ovaj niz.

Although the String part of String::toLowerCase makes it look like a class is being referenced, only an instance of this class is referenced. The anonymous class example makes this more obvious. Note that in the anonymous class example the lambda receives an argument; it doesn't close over parameter s (i.e., it's not a closure).

Compile Listing 3 and run the application. You'll observe the following output:

string to lowercase string to lowercase string to lowercase

References to constructors

You can use a method reference to refer to a constructor without instantiating the named class. This kind of method reference is known as a constructor reference. Its syntax is className::new. className must support object creation; it cannot name an abstract class or interface. Keyword new names the referenced constructor. Here are some examples:

  • Character::new: equivalent to lambda (Character ch) -> new Character(ch)
  • Long::new: equivalent to lambda (long value) -> new Long(value) or (String s) -> new Long(s)
  • ArrayList::new: equivalent to lambda () -> new ArrayList()
  • float[]::new: equivalent to lambda (int size) -> new float[size]

The last constructor reference example specifies an array type instead of a class type, but the principle is the same. The example demonstrates an array constructor reference to the "constructor" of an array type.

To create a constructor reference, specify new without a constructor. When a class such as java.lang.Long declares multiple constructors, the compiler compares the functional interface's type against all of the constructors and chooses the best match. Listing 4 demonstrates a constructor reference.

Listing 4. MRDemo.java (version 4)

import java.util.function.Supplier; public class MRDemo { public static void main(String[] args) { Supplier supplier = MRDemo::new; System.out.println(supplier.get()); } }

Listing 4's MRDemo::new constructor reference is equivalent to lambda () -> new MRDemo(). Expression supplier.get() executes this lambda, which invokes MRDemo's default no-argument constructor and returns the MRDemo object, which is passed to System.out.println(). This method converts the object to a string, which it prints.

Now suppose you have a class with a no-argument constructor and a constructor that takes an argument, and you want to call the constructor that takes an argument. You can accomplish this task by choosing a different functional interface, such as the predefined Function interface shown in Listing 5.

Listing 5. MRDemo.java (version 5)

import java.util.function.Function; public class MRDemo { private String name; MRDemo() { name = ""; } MRDemo(String name) { this.name = name; System.out.printf("MRDemo(String name) called with %s%n", name); } public static void main(String[] args) { Function function = MRDemo::new; System.out.println(function.apply("some name")); } }

Function function = MRDemo::new;uzrokuje prevodilac tražiti konstruktora koja traje Stringargument, jer Function„s apply()metoda zahtijeva jedan (u ovom kontekstu) Stringargument. Izvršavanje function.apply("some name")rezultata "some name"prosljeđivanja MRDemo(String name).