CodeGym /Kurse /JAVA 25 SELF /Funktionale Schnittstellen: @FunctionalInterface

Funktionale Schnittstellen: @FunctionalInterface

JAVA 25 SELF
Level 20, Lektion 3
Verfügbar

1. Einleitung

Eine funktionale Schnittstelle ist eine Schnittstelle, die genau eine abstrakte Methode enthält (also eine Methode ohne Implementierung). Genau solche Schnittstellen können für eine kurze Schreibweise der Methodenimplementierung verwendet werden – in Java geschieht das mit Lambda-Ausdrücken (die behandeln wir später).

Warum nur eine Methode?

Weil eine funktionale Schnittstelle genau eine Operation beschreibt. Wenn es zwei oder mehr Methoden gäbe, wäre unklar, welche Methode genau implementiert werden soll. Die Regel ist daher einfach: eine Schnittstelle – eine abstrakte Methode.

Beispiele aus der Standardbibliothek

  • Runnable – für Aufgaben in Threads (void run())
  • Callable<V> – für Aufgaben, die ein Ergebnis zurückgeben (V call())
  • Comparator<T> – zum Vergleichen von Objekten (int compare(T o1, T o2))
  • Consumer<T> – „Verbraucher“ eines Wertes (void accept(T t))
  • Supplier<T> – „Lieferant“ eines Wertes (T get())
  • Function<T, R> – Funktion von T nach R (R apply(T t))
  • Predicate<T> – Bedingungsprüfung (boolean test(T t))

Hier zum Beispiel die Schnittstelle Runnable:

public interface Runnable {
    void run();
}

Und hier die Schnittstelle Comparator:

public interface Comparator<T> {
    int compare(T o1, T o2);
    // ... es gibt noch default- und static-Methoden, aber die abstrakte Methode gibt es nur einmal!
}

Wichtiger Punkt: default- und static-Methoden gelten nicht als abstrakt, daher kann es davon beliebig viele geben!

2. Die Annotation @FunctionalInterface

Java ist eine strenge, prinzipientreue Dame. Damit es keine Verwirrung gibt, kann man eine Schnittstelle mit der Annotation @FunctionalInterface ausdrücklich als funktional markieren. Das ist wie ein Aufkleber „Funktioniert nur mit einer Taste!“ – damit niemand etwas Überflüssiges hinzufügt.

@FunctionalInterface
public interface Operation {
    int apply(int a, int b);
}

Wenn Sie nun versehentlich eine zweite abstrakte Methode hinzufügen, meldet der Compiler sofort einen Fehler:

@FunctionalInterface
public interface Oops {
    void doIt();
    void doSomethingElse(); // Fehler! Zwei abstrakte Methoden
}

Ist die Annotation zwingend?

Nein, das ist nicht erforderlich. Eine Schnittstelle ist auch ohne sie funktional, wenn sie genau eine abstrakte Methode enthält. Mit der Annotation zeigen Sie jedoch Ihre Absicht eindeutig und schützen sich vor versehentlichen Fehlern.

Darf man default und static-Methoden hinzufügen?

Ja, das darf man! Wichtig ist, dass es nur eine abstrakte Methode gibt. Alle übrigen Methoden dürfen default oder static sein, so viele Sie möchten.

Beispiel:

@FunctionalInterface
public interface FancyOperation {
    int apply(int a, int b);

    default void printInfo() {
        System.out.println("Ich bin eine fancy Operation!");
    }

    static void description() {
        System.out.println("Funktionale Schnittstelle für Arithmetik.");
    }
}

3. Beispiele für Deklaration und Verwendung

Angenommen, Sie möchten eine Operation über zwei Zahlen beschreiben. So geht das:

@FunctionalInterface
public interface Operation {
    int apply(int a, int b);
}

Diese Schnittstelle kann nun auf verschiedene Weise implementiert werden.

Implementierung über eine normale Klasse

public class SumOperation implements Operation {
    @Override
    public int apply(int a, int b) {
        return a + b;
    }
}

Verwendung:

Operation sum = new SumOperation();
System.out.println(sum.apply(2, 3)); // 5

Implementierung über eine anonyme Klasse

Operation multiply = new Operation() {
    @Override
    public int apply(int a, int b) {
        return a * b;
    }
};
System.out.println(multiply.apply(2, 3)); // 6

Hinweis zu Lambdas

Seit Java 8 lassen sich solche Schnittstellen praktisch mit Lambda-Ausdrücken implementieren – das ist eine kürzere Syntax. Wir werden Lambdas in ein paar Vorlesungen behandeln; fürs Erste genügt es zu wissen: Funktionale Schnittstellen sind genau dafür gedacht, möglichst bequem damit zu arbeiten.

4. Praxis: Schreiben Sie Ihre eigene funktionale Schnittstelle

Aufgabe 1. Erstellen Sie Ihr eigenes Action!

Erstellen Sie die Schnittstelle Action, die eine Zeichenkette entgegennimmt und nichts zurückgibt. Implementieren Sie sie über eine anonyme Klasse, die die Zeichenkette in Großbuchstaben ausgibt.

@FunctionalInterface
interface Action {
    void act(String s);
}

public class ActionDemo {
    public static void main(String[] args) {
        Action shout = new Action() {
            @Override
            public void act(String text) {
                System.out.println(text.toUpperCase());
            }
        };

        shout.act("ich lerne java!"); // ICH LERNE JAVA!
    }
}

(Später sehen wir, wie man das mit Lambda-Ausdrücken noch kürzer schreiben kann.)

Aufgabe 2. Zahlenfilter

Erstellen Sie die Schnittstelle NumberPredicate mit der Methode boolean test(int n). Implementieren Sie die Prüfung auf Gerade mit einer anonymen Klasse.

@FunctionalInterface
interface NumberPredicate {
    boolean test(int n);
}

public class PredicateDemo {
    public static void main(String[] args) {
        NumberPredicate isEven = new NumberPredicate() {
            @Override
            public boolean test(int n) {
                return n % 2 == 0;
            }
        };

        System.out.println(isEven.test(4)); // true
        System.out.println(isEven.test(7)); // false
    }
}

Aufgabe 3. Nutzung der Standard-Schnittstellen

Anstelle einer eigenen Schnittstelle kann man den fertigen Predicate<Integer> verwenden:

import java.util.function.Predicate;

Predicate<Integer> isPositive = new Predicate<Integer>() {
    @Override
    public boolean test(Integer x) {
        return x > 0;
    }
};

System.out.println(isPositive.test(10)); // true
System.out.println(isPositive.test(-5)); // false

Tabelle: Funktionale Schnittstellen aus der Standardbibliothek

Schnittstelle Methode Beschreibung Beispiel
Runnable
void run()
Aufgabe ohne Argumente und Ergebnis Threads, Timer
Callable<V>
V call()
Aufgabe mit Ergebnis Threads, ExecutorService
Comparator<T>
int compare(T, T)
Vergleich zweier Objekte Sortieren von Sammlungen
Consumer<T>
void accept(T)
„Verbraucher“ eines Wertes Iteration über eine Sammlung
Supplier<T>
T get()
„Lieferant“ eines Wertes Lazy-Initialisierung, Datengenerierung
Function<T, R>
R apply(T)
Funktion von T nach R Datenumwandlung
Predicate<T>
boolean test(T)
Bedingungsprüfung Filtern von Sammlungen

5. Typische Fehler beim Arbeiten mit funktionalen Schnittstellen

Fehler Nr. 1: eine zweite abstrakte Methode hinzugefügt. Wenn eine Schnittstelle mehr als eine abstrakte Methode hat, ist sie nicht mehr funktional. Der Compiler (insbesondere mit @FunctionalInterface) meldet den Fehler sofort.

Fehler Nr. 2: vergessen, dass default- und static-Methoden nicht als abstrakt gelten. Sie können sie bedenkenlos zu einer funktionalen Schnittstelle hinzufügen – das verletzt die Regel „genau eine abstrakte Methode“ nicht.

Fehler Nr. 3: die Methodensignatur falsch implementiert. Zum Beispiel fordert die Schnittstelle zwei Argumente, Sie haben die Methode aber nur mit einem geschrieben. Prüfen Sie immer die Signaturen.

Fehler Nr. 4: @FunctionalInterface nicht verwendet und die Schnittstelle versehentlich beschädigt. Ohne Annotation kann man versehentlich eine zusätzliche Methode hinzufügen – und dann lange suchen, warum der Code nicht funktioniert. Fügen Sie die Annotation am besten immer zur Klarheit hinzu.

1
Aufgabe
JAVA 25 SELF, Level 20, Lektion 3
Gesperrt
Ihr persönlicher Notifikator
Ihr persönlicher Notifikator
1
Aufgabe
JAVA 25 SELF, Level 20, Lektion 3
Gesperrt
Spiel-Checker: Hohe Punktzahl
Spiel-Checker: Hohe Punktzahl
1
Aufgabe
JAVA 25 SELF, Level 20, Lektion 3
Gesperrt
Textvalidator: Überprüfung der String-Länge
Textvalidator: Überprüfung der String-Länge
1
Aufgabe
JAVA 25 SELF, Level 20, Lektion 3
Gesperrt
Textkonverter mit praktischer Ausgabe
Textkonverter mit praktischer Ausgabe
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION