JavaRush /Java Blog /Random-TL /Mga Functional na Interface sa Java

Mga Functional na Interface sa Java

Nai-publish sa grupo
Kamusta! Sa Java Syntax Pro quest, pinag-aralan namin ang mga expression ng lambda at sinabi na ang mga ito ay walang iba kundi isang pagpapatupad ng isang functional na paraan mula sa isang functional na interface. Sa madaling salita, ito ay ang pagpapatupad ng ilang hindi kilalang (hindi kilalang) klase, ang hindi natanto na pamamaraan nito. At kung sa mga lektura ng kurso ay hinanap natin ang mga manipulasyon na may mga ekspresyong lambda, ngayon ay isasaalang-alang natin, wika nga, ang kabilang panig: ibig sabihin, ang mga mismong interface na ito. Mga Functional na Interface sa Java - 1Ipinakilala ng ikawalong bersyon ng Java ang konsepto ng mga functional na interface . Ano ito? Ang isang interface na may isang hindi naipatupad (abstract) na pamamaraan ay itinuturing na gumagana. Maraming out-of-the-box na interface ang nasa ilalim ng kahulugang ito, gaya ng, halimbawa, ang dating tinalakay na interface Comparator. At gayundin ang mga interface na nilikha namin sa aming sarili, tulad ng:
@FunctionalInterface
public interface Converter<T, N> {
   N convert(T t);
}
Mayroon kaming isang interface na ang gawain ay i-convert ang mga bagay ng isang uri sa mga bagay ng isa pa (isang uri ng adaptor). Ang anotasyon @FunctionalInterfaceay hindi isang bagay na sobrang kumplikado o mahalaga, dahil ang layunin nito ay sabihin sa compiler na ang interface na ito ay gumagana at hindi dapat maglaman ng higit sa isang paraan. Kung ang isang interface na may ganitong anotasyon ay may higit sa isang hindi naipatupad na (abstract) na pamamaraan, hindi laktawan ng compiler ang interface na ito, dahil iisipin ito bilang maling code. Ang mga interface na walang annotation na ito ay maaaring ituring na gumagana at gagana, ngunit @FunctionalInterfaceito ay walang iba kundi karagdagang insurance. Balik na tayo sa klase Comparator. Kung titingnan mo ang code nito (o dokumentasyon ), makikita mo na mayroon itong higit sa isang paraan. Pagkatapos ay itatanong mo: paano, kung gayon, maaari itong ituring na isang functional na interface? Ang mga abstract na interface ay maaaring magkaroon ng mga pamamaraan na wala sa saklaw ng isang paraan:
  • static
Ang konsepto ng mga interface ay nagpapahiwatig na ang isang naibigay na yunit ng code ay hindi maaaring magkaroon ng anumang mga pamamaraan na ipinatupad. Ngunit simula sa Java 8, naging posible na gumamit ng mga static at default na pamamaraan sa mga interface. Ang mga static na pamamaraan ay direktang nakatali sa isang klase at hindi nangangailangan ng isang partikular na bagay ng klase na iyon upang tawagan ang gayong pamamaraan. Iyon ay, ang mga pamamaraang ito ay magkatugma sa konsepto ng mga interface. Bilang isang halimbawa, magdagdag tayo ng isang static na pamamaraan para sa pagsuri ng isang bagay para sa null sa nakaraang klase:
@FunctionalInterface
public interface Converter<T, N> {

   N convert(T t);

   static <T> boolean isNotNull(T t){
       return t != null;
   }
}
Nang matanggap ang pamamaraang ito, hindi nagreklamo ang compiler, na nangangahulugang gumagana pa rin ang aming interface.
  • mga default na pamamaraan
Bago ang Java 8, kung kailangan naming lumikha ng isang pamamaraan sa isang interface na minana ng ibang mga klase, maaari lamang kaming lumikha ng isang abstract na pamamaraan na ipinatupad sa bawat partikular na klase. Ngunit paano kung ang pamamaraang ito ay pareho para sa lahat ng mga klase? Sa kasong ito , ang mga abstract na klase ay kadalasang ginagamit . Ngunit simula sa Java 8, mayroong isang opsyon na gumamit ng mga interface na may mga ipinatupad na pamamaraan - ang mga default na pamamaraan. Kapag nagmana ng isang interface, maaari mong i-override ang mga pamamaraan na ito o iwanan ang lahat ng bagay (iwanan ang default na lohika). Kapag lumilikha ng default na paraan, dapat nating idagdag ang keyword - default:
@FunctionalInterface
public interface Converter<T, N> {

   N convert(T t);

   static <T> boolean isNotNull(T t){
       return t != null;
   }

   default void writeToConsole(T t) {
       System.out.println("Текущий an object - " + t.toString());
   }
}
Muli, nakita namin na ang compiler ay hindi nagsimulang magreklamo, at hindi kami lumampas sa mga limitasyon ng functional interface.
  • Mga pamamaraan ng klase ng object
Sa lecture na Paghahambing ng mga Bagay , napag-usapan namin ang katotohanan na ang lahat ng mga klase ay nagmamana mula sa klase Object. Hindi ito nalalapat sa mga interface. Ngunit kung mayroon kaming abstract na pamamaraan sa interface na tumutugma sa lagda sa ilang paraan ng class Object, hindi masisira ng ganoong paraan (o mga pamamaraan) ang aming functional interface restriction:
@FunctionalInterface
public interface Converter<T, N> {

   N convert(T t);

   static <T> boolean isNotNull(T t){
       return t != null;
   }

   default void writeToConsole(T t) {
       System.out.println("Текущий an object - " + t.toString());
   }

   boolean equals(Object obj);
}
At muli, ang aming compiler ay hindi nagrereklamo, kaya ang interface Converteray itinuturing pa rin na gumagana. Ngayon ang tanong ay: bakit kailangan nating limitahan ang ating sarili sa isang hindi naipatupad na pamamaraan sa isang functional na interface? At pagkatapos ay upang maipatupad natin ito gamit ang mga lambdas. Tingnan natin ito sa isang halimbawa Converter. Upang gawin ito, lumikha tayo ng isang klase Dog:
public class Dog {
  String name;
  int age;
  int weight;

  public Dog(final String name, final int age, final int weight) {
     this.name = name;
     this.age = age;
     this.weight = weight;
  }
}
At isang katulad Raccoon(raccoon):
public class Raccoon {
  String name;
  int age;
  int weight;

  public Raccoon(final String name, final int age, final int weight) {
     this.name = name;
     this.age = age;
     this.weight = weight;
  }
}
Ipagpalagay na mayroon tayong isang bagay Dog, at kailangan nating lumikha ng isang bagay batay sa mga patlang nito Raccoon. Iyon ay, Converterpinapalitan nito ang isang bagay ng isang uri sa isa pa. Paano ito magiging hitsura:
public static void main(String[] args) {
  Dog dog = new Dog("Bobbie", 5, 3);

  Converter<Dog, Raccoon> converter = x -> new Raccoon(x.name, x.age, x.weight);

  Raccoon raccoon = converter.convert(dog);

  System.out.println("Raccoon has parameters: name - " + raccoon.name + ", age - " + raccoon.age + ", weight - " + raccoon.weight);
}
Kapag pinatakbo namin ito, nakukuha namin ang sumusunod na output sa console:

Raccoon has parameters: name - Bobbbie, age - 5, weight - 3
At nangangahulugan ito na gumana nang tama ang aming pamamaraan.Mga Functional na Interface sa Java - 2

Pangunahing Java 8 Mga Pang-andar na Interface

Ngayon, tingnan natin ang ilang functional na interface na dinala sa atin ng Java 8 at aktibong ginagamit kasabay ng Stream API.

panaguri

Predicate— isang functional na interface para sa pagsuri kung ang isang partikular na kundisyon ay natutugunan. Kung natugunan ang kundisyon, babalik true, kung hindi - false:
@FunctionalInterface
public interface Predicate<T> {
   boolean test(T t);
}
Bilang isang halimbawa, isaalang-alang ang paglikha ng isang Predicatena magsusuri ng parity ng isang bilang ng uri Integer:
public static void main(String[] args) {
   Predicate<Integer> isEvenNumber = x -> x % 2==0;

   System.out.println(isEvenNumber.test(4));
   System.out.println(isEvenNumber.test(3));
}
Output ng console:

true
false

Konsyumer

Consumer(mula sa English - "consumer") - isang functional na interface na kumukuha ng isang object ng uri T bilang isang input argument, nagsasagawa ng ilang mga aksyon, ngunit walang ibinalik:
@FunctionalInterface
public interface Consumer<T> {
   void accept(T t);
}
Bilang halimbawa, isaalang-alang ang , na ang gawain ay maglabas ng pagbati sa console na may ipinasa na argumentong string: Consumer
public static void main(String[] args) {
   Consumer<String> greetings = x -> System.out.println("Hello " + x + " !!!");
   greetings.accept("Elena");
}
Output ng console:

Hello Elena !!!

Supplier

Supplier(mula sa English - provider) - isang functional na interface na hindi kumukuha ng anumang mga argumento, ngunit nagbabalik ng object ng uri T:
@FunctionalInterface
public interface Supplier<T> {
   T get();
}
Bilang halimbawa, isaalang-alang ang Supplier, na gagawa ng mga random na pangalan mula sa isang listahan:
public static void main(String[] args) {
   ArrayList<String> nameList = new ArrayList<>();
   nameList .add("Elena");
   nameList .add("John");
   nameList .add("Alex");
   nameList .add("Jim");
   nameList .add("Sara");

   Supplier<String> randomName = () -> {
       int value = (int)(Math.random() * nameList.size());
       return nameList.get(value);
   };

   System.out.println(randomName.get());
}
At kung patakbuhin natin ito, makikita natin ang mga random na resulta mula sa isang listahan ng mga pangalan sa console.

Function

Function— ang functional na interface na ito ay kumukuha ng argumentong T at inihagis ito sa isang bagay ng uri R, na ibinalik bilang resulta:
@FunctionalInterface
public interface Function<T, R> {
   R apply(T t);
}
Bilang halimbawa, kunin natin ang , na nagko-convert ng mga numero mula sa format ng string ( ) sa format ng numero ( ): FunctionStringInteger
public static void main(String[] args) {
   Function<String, Integer> valueConverter = x -> Integer.valueOf(x);
   System.out.println(valueConverter.apply("678"));
}
Kapag pinatakbo namin ito, nakukuha namin ang sumusunod na output sa console:

678
PS: kung ipapasa namin hindi lamang ang mga numero, kundi pati na rin ang iba pang mga character sa string, isang pagbubukod ang itatapon - NumberFormatException.

UnaryOperator

UnaryOperator— isang functional na interface na kumukuha ng isang bagay ng uri T bilang isang parameter, nagsasagawa ng ilang mga operasyon dito at ibinabalik ang resulta ng mga operasyon sa anyo ng isang bagay ng parehong uri ng T:
@FunctionalInterface
public interface UnaryOperator<T> {
   T apply(T t);
}
UnaryOperator, na gumagamit ng pamamaraan nito applyupang i-square ang isang numero:
public static void main(String[] args) {
   UnaryOperator<Integer> squareValue = x -> x * x;
   System.out.println(squareValue.apply(9));
}
Output ng console:

81
Tumingin kami sa limang functional na interface. Hindi lang ito ang magagamit sa amin simula sa Java 8 - ito ang mga pangunahing interface. Ang natitirang mga magagamit ay ang kanilang mga kumplikadong analogues. Ang kumpletong listahan ay matatagpuan sa opisyal na dokumentasyon ng Oracle .

Mga functional na interface sa Stream

Gaya ng tinalakay sa itaas, ang mga functional na interface na ito ay mahigpit na pinagsama sa Stream API. Paano, tanong mo? Mga Functional na Interface sa Java - 3At tulad na maraming mga pamamaraan Streamang partikular na gumagana sa mga functional na interface na ito. Tingnan natin kung paano magagamit ang mga functional na interface sa Stream.

Pamamaraan na may panaguri

Halimbawa, kunin natin ang paraan ng klase Stream- filterna tumatagal bilang argumento Predicateat ibinabalik Streamlamang ang mga elementong iyon na nakakatugon sa kundisyon Predicate. Sa konteksto ng Stream-a, nangangahulugan ito na dumadaan lamang ito sa mga elementong iyon na ibinalik truekapag ginamit sa isang paraan testng interface Predicate. Ito ang magiging hitsura ng aming halimbawa para sa Predicate, ngunit para sa isang filter ng mga elemento sa Stream:
public static void main(String[] args) {
   List<Integer> evenNumbers = Stream.of(1, 2, 3, 4, 5, 6, 7, 8)
           .filter(x -> x % 2==0)
           .collect(Collectors.toList());
}
Bilang resulta, ang listahan evenNumbersay bubuo ng mga elemento {2, 4, 6, 8}. At, gaya ng naaalala namin, collectkokolektahin nito ang lahat ng elemento sa isang partikular na koleksyon: sa aming kaso, sa List.

Pamamaraan sa Consumer

Isa sa mga pamamaraan sa Stream, na gumagamit ng functional interface Consumer, ay ang peek. Ito ang magiging hitsura ng aming halimbawa Consumersa Stream:
public static void main(String[] args) {
   List<String> peopleGreetings = Stream.of("Elena", "John", "Alex", "Jim", "Sara")
           .peek(x -> System.out.println("Hello " + x + " !!!"))
           .collect(Collectors.toList());
}
Output ng console:

Hello Elena !!!
Hello John !!!
Hello Alex !!!
Hello Jim !!!
Hello Sara !!!
Ngunit dahil gumagana ang pamamaraan peeksa Consumer, ang pagbabago ng mga string sa Streamay hindi magaganap, ngunit peekbabalik Streamkasama ang mga orihinal na elemento: katulad ng pagdating nila dito. Samakatuwid, ang listahan peopleGreetingsay binubuo ng mga elementong "Elena", "John", "Alex", "Jim", "Sara". Mayroon ding karaniwang ginagamit na paraan foreach, na katulad ng pamamaraan peek, ngunit ang pagkakaiba ay ito ay panghuling - terminal.

Paraan sa Supplier

Ang isang halimbawa ng isang paraan Streamna gumagamit ng functional na interface Supplieray ang generate, na bumubuo ng isang walang katapusang sequence batay sa functional na interface na ipinasa dito. Gamitin natin ang ating halimbawa Supplierpara mag-print ng limang random na pangalan sa console:
public static void main(String[] args) {
   ArrayList<String> nameList = new ArrayList<>();
   nameList.add("Elena");
   nameList.add("John");
   nameList.add("Alex");
   nameList.add("Jim");
   nameList.add("Sara");

   Stream.generate(() -> {
       int value = (int) (Math.random() * nameList.size());
       return nameList.get(value);
   }).limit(5).forEach(System.out::println);
}
At ito ang output na nakukuha namin sa console:

John
Elena
Elena
Elena
Jim
Dito ginamit namin ang pamamaraan limit(5)upang magtakda ng limitasyon sa pamamaraan generate, kung hindi ay magpi-print ang programa ng mga random na pangalan sa console nang walang katapusan.

Paraan na may Function

Ang isang tipikal na halimbawa ng isang pamamaraan na may Streamargumento Functionay isang paraan mapna kumukuha ng mga elemento ng isang uri, gumagawa ng isang bagay sa kanila at ipinapasa ang mga ito, ngunit ang mga ito ay maaaring mga elemento ng ibang uri. Ano ang hitsura ng isang halimbawa na may Functionin Stream:
public static void main(String[] args) {
   List<Integer> values = Stream.of("32", "43", "74", "54", "3")
           .map(x -> Integer.valueOf(x)).collect(Collectors.toList());
}
Bilang resulta, nakakakuha kami ng listahan ng mga numero, ngunit sa Integer.

Paraan sa UnaryOperator

Bilang isang pamamaraan na ginagamit UnaryOperatorbilang isang argumento, kunin natin ang isang paraan ng klase Stream- iterate. Ang pamamaraang ito ay katulad ng pamamaraan generate: bumubuo rin ito ng walang katapusang pagkakasunud-sunod ngunit may dalawang argumento:
  • ang una ay ang elemento kung saan nagsisimula ang sequence generation;
  • ang pangalawa ay UnaryOperator, na nagpapahiwatig ng prinsipyo ng pagbuo ng mga bagong elemento mula sa unang elemento.
Ito ang magiging hitsura ng aming halimbawa UnaryOperator, ngunit sa pamamaraan iterate:
public static void main(String[] args) {
   Stream.iterate(9, x -> x * x)
           .limit(4)
           .forEach(System.out::println);
}
Kapag pinatakbo namin ito, nakukuha namin ang sumusunod na output sa console:

9
81
6561
43046721
Iyon ay, ang bawat isa sa aming mga elemento ay pinarami ng sarili nito, at iba pa para sa unang apat na numero. Mga Functional na Interface sa Java - 4Iyon lang! Magiging mahusay kung pagkatapos basahin ang artikulong ito ikaw ay isang hakbang na mas malapit sa pag-unawa at mastering ang Stream API sa Java!
Mga komento
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION