Interfejsy funkcyjne
Część sekcji Programowanie obiektowe ścieżki Java w Coddy. Lekcja 39 z 87.
Interfejs funkcyjny to interfejs zawierający dokładnie jedną metodę abstrakcyjną. To ograniczenie do jednej metody sprawia, że interfejsy funkcyjne są wyjątkowe, ponieważ można je implementować za pomocą wyrażeń lambda, co umożliwia zwięźlejszy styl programowania.
Java udostępnia adnotację @FunctionalInterface, która jawnie oznacza interfejs jako funkcyjny. Choć jest opcjonalna, ta adnotacja pomaga kompilatorowi wykrywać błędy, jeśli przypadkowo dodasz drugą metodę abstrakcyjną:
@FunctionalInterface
public interface Calculator {
int calculate(int a, int b);
}Możesz zaimplementować interfejs funkcyjny w tradycyjny sposób za pomocą klasy lub zwięźlej, używając wyrażenia lambda:
// Tradycyjna implementacja
Calculator adder = new Calculator() {
@Override
public int calculate(int a, int b) {
return a + b;
}
};
// Wyrażenie lambda (znacznie krótsze!)
Calculator adder = (a, b) -> a + b;
System.out.println(adder.calculate(5, 3)); // 8Interfejsy funkcyjne mogą nadal mieć metody domyślne i statyczne, ponieważ nie wliczają się one do zasady jednej metody abstrakcyjnej. Pakiet Java java.util.function udostępnia wiele wbudowanych interfejsów funkcyjnych, takich jak Predicate, Function i Consumer, z którymi często się spotkasz podczas pracy ze strumieniami i kolekcjami.
Wyzwanie
ŁatwyZbudujmy system przekształcania ciągów znaków przy użyciu interfejsów funkcyjnych i wyrażeń lambda. Utworzysz elastyczny procesor tekstu, który może stosować różne przekształcenia do ciągów znaków — wszystko dzięki jednemu interfejsowi funkcyjnemu.
Rozmieścisz swój kod w trzech plikach:
StringTransformer.java: Zdefiniuj interfejs funkcyjny reprezentujący dowolną operację przekształcania ciągu znaków. Oznacz go adnotacją@FunctionalInterface, aby mieć pewność, że pozostanie funkcyjny. Twój interfejs powinien deklarować jedną metodę abstrakcyjną o nazwietransform, która przyjmuje String i zwraca String.TextProcessor.java: Utwórz klasę, która używa twojego interfejsu funkcyjnego do przetwarzania tekstu. Powinna mieć pole przechowująceStringTransformeroraz konstruktor, który je inicjalizuje. Dodaj metodę o nazwieprocess(String text), która stosuje transformator do podanego tekstu i zwraca wynik. Dzięki takiemu projektowi możesz przekazać dowolne zachowanie przekształcające za pomocą konstruktora.Main.java: Połącz wszystko, tworząc różne przekształcenia za pomocą wyrażeń lambda. Otrzymasz dwa dane wejściowe: ciąg tekstowy oraz typ operacji (upper,lowerlubreverse).Na podstawie typu operacji utwórz obiekt
TextProcessorz odpowiednią lambdą:upper: przekształca tekst na wielkie literylower: przekształca tekst na małe literyreverse: odwraca tekst (podpowiedź: użyjStringBuilder)
Następnie wywołaj
process()z tekstem wejściowym i wyświetl wynik w tym formacie:Result: [transformed text]
Otrzymasz dwa dane wejściowe: tekst do przekształcenia (String) oraz typ operacji (String).
Zauważ, że ta sama klasa TextProcessor może wykonywać zupełnie różne operacje w zależności od tego, którą lambdę jej przekażesz — na tym polega siła interfejsów funkcyjnych!
Spróbuj swoich sił
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String text = scanner.nextLine();
String operation = scanner.nextLine();
TextProcessor processor;
// TODO: Na podstawie typu operacji utwórz TextProcessor z odpowiednią lambdą:
// - "upper": zamienia tekst na wielkie litery
// - "lower": zamienia tekst na małe litery
// - "reverse": odwraca tekst (wskazówka: użyj StringBuilder)
// TODO: Wywołaj process() z tekstem wejściowym i wypisz wynik
// Format wyjściowy: "Result: [transformed text]"
}
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy programowania obiektowego
Pliki zewnętrzneWprowadzenie do programowania obiektowegoKlasy a obiektySłowo kluczowe thisMetodyPola (atrybuty)Metoda konstruktoraPrzeciążanie konstruktoraPodsumowanie — prosty kalkulator4Dziedziczenie
Podstawy dziedziczenia (extends)Słowo kluczowe superPrzesłanianie metod (@Override)Łańcuch wywołań konstruktorówKlasa ObjectDziedziczenie jedno- i wielopoziomoweDlaczego Java nie obsługuje wielodziedziczenia klasPodsumowanie – hierarchia pracowników7Metody specjalne i klasa Object
Metoda toString()equals() i hashCode()Metoda clone()compareTo() i ComparableInterfejs ComparatorPodsumowanie — własne sortowanie10Obsługa wyjątków w programowaniu obiektowym
Hierarchia klas wyjątkówWłasne wyjątkiBłędy sprawdzane a niesprawdzaneWzorzec try-with-resourcesPodsumowanie – zweryfikowany użytkownik2Modyfikatory dostępu i enkapsulacja
Przegląd poziomów dostępuMetody pobierające i ustawiająceUkrywanie informacjiSłowo kluczowe finalPodsumowanie — menedżer kont bankowych5Polimorfizm
Podstawy przeciążania metodPrzesłanianie metod (w czasie wykonywania)Rzutowanie w górę i w dółOperator instanceofKlasy i metody abstrakcyjnePowtórka — kalkulator figur8Zaawansowane koncepcje OOP
Kompozycja a dziedziczenieAgregacja a kompozycjaKlasy wewnętrzne, zagnieżdżone i anonimoweEnumy i metody enumówRekordy (Java 16+)Klasy zapieczętowane (Java 17+)11Wzorce projektowe — część 1
Wprowadzenie do wzorców projektowychWzorzec SingletonWzorzec FactoryWzorzec BuilderWzorzec ObserverWzorzec Strategy3Właściwości klas i składowe statyczne
Zmienne instancji a zmienne statyczneMetody statyczneBloki statyczneStałe (static final)Powtórka – Counter i Utility6Interfejsy i klasy abstrakcyjne
Wprowadzenie do interfejsówImplementowanie interfejsówImplementacja wielu interfejsówMetody domyślne i statyczne w interfejsieKlasy abstrakcyjne a interfejsyInterfejsy funkcyjnePodsumowanie — system płatnościPoćwicz samodzielnie: Kompilator Java online