Java Interface
Ein Interface (Schnittstelle) in Java ist ein Vertrag, der definiert, welche Methoden eine Klasse implementieren muss, aber nicht wie.
Interfaces ermöglichen Polymorphismus und Multiple Inheritance (Mehrfachvererbung von Verhalten), da eine Klasse mehrere Interfaces implementieren kann.
Bei Java Vererbung kann eine Child-Klasse nur von einer Parent-Klasse erben.
Mit Interfaces kann eine Klasse mehrere Interfaces implementieren und somit Mehrfachvererbung simulieren.
Grundkonzept
Was ist ein Interface?
Ein Interface ist wie ein Bauplan oder Vertrag:
- Definiert was eine Klasse können muss
- Definiert nicht wie es gemacht wird
- Enthält nur Methodensignaturen (vor Java 8)
- Kann keine Instanzvariablen haben (nur Konstanten)
Interface = Führerschein-Prüfung
- Der Führerschein sagt: "Du musst Auto fahren, bremsen und parken können"
- Aber er sagt NICHT, WIE du das machst
- Jeder Fahrer implementiert es unterschiedlich
Syntax
Interface definieren
public interface InterfaceName {
// Abstrakte Methoden (implizit public abstract)
void methode1();
int methode2(String parameter);
// Konstanten (implizit public static final)
int MAX_VALUE = 100;
}
Interface implementieren
public class KlassenName implements Interface1, Interface2 {
// MUSS alle Methoden implementieren
@Override
public void methode1() {
// Implementierung
}
@Override
public int methode2(String parameter) {
// Implementierung
return 42;
}
}
Eine Klasse, die ein Interface implementiert, MUSS alle Methoden des Interfaces überschreiben, sonst gibt es einen Compiler-Fehler.
Praktisches Beispiel: Prey und Predator
// Interface für Beutetiere
public interface Prey {
void flee(); // Beutetiere können fliehen
}
// Interface für Raubtiere
public interface Predator {
void hunt(); // Raubtiere können jagen
}
// Rabbit ist nur Beute
public class Rabbit implements Prey {
@Override
public void flee() {
System.out.println("🐰 Der Hase flieht!");
}
}
// Wolf ist nur Raubtier
public class Wolf implements Predator {
@Override
public void hunt() {
System.out.println("🐺 Der Wolf jagt!");
}
}
// Fish kann BEIDES sein (Multiple Inheritance!)
public class Fish implements Predator, Prey {
private boolean isPredator;
public Fish(boolean isPredator) {
this.isPredator = isPredator;
}
@Override
public void flee() {
if (!isPredator) {
System.out.println("🐟 Der Fisch flieht!");
} else {
System.out.println("🦈 Dieser Fisch jagt und flieht nicht!");
}
}
@Override
public void hunt() {
if (isPredator) {
System.out.println("🦈 Der Fisch jagt kleinere Fische!");
} else {
System.out.println("🐟 Dieser Fisch ist ein Beutetier!");
}
}
}
// Verwendung
public class Main {
public static void main(String[] args) {
Rabbit rabbit = new Rabbit();
rabbit.flee(); // 🐰 Der Hase flieht!
Wolf wolf = new Wolf();
wolf.hunt(); // 🐺 Der Wolf jagt!
Fish predatorFish = new Fish(true);
predatorFish.hunt(); // 🦈 Der Fisch jagt kleinere Fische!
Fish preyFish = new Fish(false);
preyFish.flee(); // 🐟 Der Fisch flieht!
}
}
Der Fish kann beide Interfaces implementieren, weil Interfaces Mehrfachvererbung ermöglichen!
Interface-Eigenschaften
Methoden in Interfaces
Abstrakte Methoden (Standard)
Methoden ohne Implementierung (bis Java 8).
public interface Drawable {
void draw(); // Abstrakt (implizit public abstract)
}
Default-Methoden (seit Java 8)
Methoden mit Implementierung, können überschrieben werden.
public interface Drawable {
// Abstrakte Methode
void draw();
// Default-Methode (mit Implementierung)
default void display() {
System.out.println("Anzeige auf Bildschirm");
}
}
public class Circle implements Drawable {
@Override
public void draw() {
System.out.println("Zeichne Kreis");
}
// display() muss nicht überschrieben werden (optional)
}
Statische Methoden (seit Java 8)
Utility-Methoden, die zum Interface gehören.
public interface MathOperations {
static int add(int a, int b) {
return a + b;
}
static int multiply(int a, int b) {
return a * b;
}
}
// Verwendung
int result = MathOperations.add(5, 3); // 8
Private Methoden (seit Java 9)
Hilfsmethoden für default-Methoden.
public interface Logger {
default void logInfo(String message) {
log("INFO", message);
}
default void logError(String message) {
log("ERROR", message);
}
// Private Hilfsmethode
private void log(String level, String message) {
System.out.println("[" + level + "] " + message);
}
}
Konstanten in Interfaces
Alle Variablen in Interfaces sind automatisch
public static final(Konstanten).
public interface GameSettings {
// Implizit: public static final
int MAX_PLAYERS = 4;
String GAME_NAME = "Super Game";
double VERSION = 1.5;
}
// Verwendung
System.out.println(GameSettings.MAX_PLAYERS); // 4
Konstanten werden in GROSSBUCHSTABEN geschrieben mit Unterstrichen: MAX_PLAYERS
Vererbung bei Interfaces
Interface erweitert Interface
Interfaces können andere Interfaces erweitern (
extends).
public interface Animal {
void eat();
}
public interface Mammal extends Animal {
void breathe();
}
public class Dog implements Mammal {
@Override
public void eat() {
System.out.println("Hund frisst");
}
@Override
public void breathe() {
System.out.println("Hund atmet");
}
}
Multiple Interface Inheritance
public interface Flyable {
void fly();
}
public interface Swimmable {
void swim();
}
// Duck kann sowohl fliegen als auch schwimmen
public class Duck implements Flyable, Swimmable {
@Override
public void fly() {
System.out.println("🦆 Ente fliegt");
}
@Override
public void swim() {
System.out.println("🦆 Ente schwimmt");
}
}
Interface vs. Abstrakte Klasse
| Eigenschaft | Interface | Java abstrakte Klasse |
|---|---|---|
| Methoden | Nur abstrakt (vor Java 8) | Abstrakt + konkret |
| Attribute | Nur Konstanten (final) |
Normale Instanzvariablen |
| Konstruktor | ❌ Kein Konstruktor | ✅ Hat Konstruktor |
| Mehrfachvererbung | ✅ Ja (mehrere Interfaces) | ❌ Nein (nur eine Klasse) |
| Zugriffsmodifikatoren | Alles public |
public, protected, private |
| Verwendung | "kann"-Beziehung | "ist-ein"-Beziehung |
| Keyword | implements |
extends |
Interface verwenden, wenn:
- ✅ Du eine "kann"-Beziehung modellierst (Flyable, Swimmable)
- ✅ Mehrere Interfaces kombiniert werden sollen
- ✅ Keine gemeinsame Implementierung benötigt wird
Abstrakte Klasse verwenden, wenn:
- ✅ Du eine "ist-ein"-Beziehung modellierst (Animal, Vehicle)
- ✅ Gemeinsame Implementierung geteilt werden soll
- ✅ Instanzvariablen benötigt werden
Polymorphismus mit Interfaces
Interfaces ermöglichen Polymorphismus - verschiedene Objekte können über das gleiche Interface angesprochen werden.
public interface Playable {
void play();
}
public class Guitar implements Playable {
@Override
public void play() {
System.out.println("🎸 Spiele Gitarre");
}
}
public class Piano implements Playable {
@Override
public void play() {
System.out.println("🎹 Spiele Klavier");
}
}
public class Drum implements Playable {
@Override
public void play() {
System.out.println("🥁 Spiele Schlagzeug");
}
}
// Polymorphismus in Aktion
public class Main {
public static void main(String[] args) {
// Array von verschiedenen Instrumenten
Playable[] instruments = {
new Guitar(),
new Piano(),
new Drum()
};
// Alle spielen ihre Musik
for (Playable instrument : instruments) {
instrument.play();
}
}
}
Ausgabe:
🎸 Spiele Gitarre
🎹 Spiele Klavier
🥁 Spiele Schlagzeug
Du kannst neue Instrumente hinzufügen, ohne den bestehenden Code zu ändern!
Praktisches Beispiel: Payment System
// Interface für Zahlungsmethoden
public interface PaymentMethod {
boolean pay(double amount);
String getPaymentType();
}
// Kreditkartenzahlung
public class CreditCard implements PaymentMethod {
private String cardNumber;
private double balance;
public CreditCard(String cardNumber, double balance) {
this.cardNumber = cardNumber;
this.balance = balance;
}
@Override
public boolean pay(double amount) {
if (balance >= amount) {
balance -= amount;
System.out.println("💳 Zahlung von " + amount + "€ mit Kreditkarte");
return true;
}
System.out.println("❌ Nicht genug Guthaben");
return false;
}
@Override
public String getPaymentType() {
return "Kreditkarte";
}
}
// PayPal-Zahlung
public class PayPal implements PaymentMethod {
private String email;
private double balance;
public PayPal(String email, double balance) {
this.email = email;
this.balance = balance;
}
@Override
public boolean pay(double amount) {
if (balance >= amount) {
balance -= amount;
System.out.println("💰 Zahlung von " + amount + "€ mit PayPal");
return true;
}
System.out.println("❌ Nicht genug Guthaben");
return false;
}
@Override
public String getPaymentType() {
return "PayPal";
}
}
// Shop-System
public class Shop {
public void checkout(PaymentMethod payment, double amount) {
System.out.println("Verarbeite Zahlung mit: " + payment.getPaymentType());
if (payment.pay(amount)) {
System.out.println("✅ Zahlung erfolgreich!");
} else {
System.out.println("❌ Zahlung fehlgeschlagen!");
}
}
}
// Verwendung
public class Main {
public static void main(String[] args) {
Shop shop = new Shop();
PaymentMethod cc = new CreditCard("1234-5678-9012", 500.0);
PaymentMethod pp = new PayPal("[email protected]", 100.0);
shop.checkout(cc, 50.0); // Kreditkarte
shop.checkout(pp, 30.0); // PayPal
}
}
- ✅ Neue Zahlungsmethoden einfach hinzufügbar (Bitcoin, Apple Pay, etc.)
- ✅ Shop-Code muss nicht geändert werden
- ✅ Alle Zahlungsmethoden über gleiche Schnittstelle
- ✅ Testbar (Mock-Zahlungsmethoden)
Functional Interfaces (seit Java 8)
Ein Functional Interface hat genau eine abstrakte Methode und kann als Lambda-Ausdruck verwendet werden.
@FunctionalInterface
public interface Calculator {
int calculate(int a, int b);
}
// Verwendung mit Lambda
public class Main {
public static void main(String[] args) {
// Addition
Calculator add = (a, b) -> a + b;
System.out.println(add.calculate(5, 3)); // 8
// Multiplikation
Calculator multiply = (a, b) -> a * b;
System.out.println(multiply.calculate(5, 3)); // 15
// Subtraktion
Calculator subtract = (a, b) -> a - b;
System.out.println(subtract.calculate(5, 3)); // 2
}
}
Bekannte Functional Interfaces:
Runnable-void run()Callable<V>-V call()Comparator<T>-int compare(T o1, T o2)Predicate<T>-boolean test(T t)Function<T,R>-R apply(T t)Consumer<T>-void accept(T t)
Best Practices
- ✅ Kleine Interfaces - Single Responsibility Principle
- ✅ Sprechende Namen -
Readable,Closeable,Serializable - ✅ "-able" Suffix - Zeigt Fähigkeit an
- ✅ Dokumentation - JavaDoc für jede Methode
- ✅ @FunctionalInterface bei Functional Interfaces
- ❌ Zu große Interfaces (God Interface)
- ❌ Interfaces nur als Marker (besser: Annotations)
- ❌ Implementierung in Interface (außer default-Methoden)
- ❌ Vergessen
@Overridezu nutzen
Interface-Segregation Principle (ISP)
SOLID-Prinzip: Klassen sollten nicht gezwungen werden, Methoden zu implementieren, die sie nicht brauchen.
❌ Schlecht:
public interface Worker {
void work();
void eat();
void sleep();
}
// Robot muss eat() und sleep() implementieren, braucht es aber nicht!
public class Robot implements Worker {
public void work() { /* ... */ }
public void eat() { /* Robots essen nicht! */ }
public void sleep() { /* Robots schlafen nicht! */ }
}
✅ Gut:
public interface Workable {
void work();
}
public interface Eatable {
void eat();
}
public interface Sleepable {
void sleep();
}
// Human implementiert alles
public class Human implements Workable, Eatable, Sleepable {
public void work() { /* ... */ }
public void eat() { /* ... */ }
public void sleep() { /* ... */ }
}
// Robot nur Workable
public class Robot implements Workable {
public void work() { /* ... */ }
}
Marker Interfaces
Interfaces ohne Methoden, die nur zur Markierung dienen.
// Marker Interface
public interface Serializable {
// Keine Methoden!
}
public class User implements Serializable {
// Diese Klasse kann serialisiert werden
}
Bekannte Marker Interfaces:
Serializable- Objekt kann serialisiert werdenCloneable- Objekt kann geklont werdenRemote- RMI-Objekt
Heute werden oft Annotations statt Marker Interfaces verwendet:
@Serializable
public class User { }
UML-Darstellung
In UML-Klassendiagrammen werden Interfaces mit
<<interface>>gekennzeichnet.
┌─────────────────┐
│ <<interface>> │
│ Playable │
├─────────────────┤
│ + play(): void │
└─────────────────┘
△
│ implements
┌──────┴──────┐
│ │
┌─────────┐ ┌─────────┐
│ Guitar │ │ Piano │
├─────────┤ ├─────────┤
│+ play() │ │+ play() │
└─────────┘ └─────────┘
Symbol: Gestrichelte Linie mit offenem Dreieck zeigt "implements"
Zusammenfassung
- Interface = Vertrag, der definiert was (nicht wie)
- Klasse kann mehrere Interfaces implementieren
- Alle Methoden sind
public abstract(Standard) - Seit Java 8:
defaultundstaticMethoden möglich - Ermöglicht Polymorphismus und Mehrfachvererbung
- Verwende Interfaces für "kann"-Beziehungen
- Abstakte Klassen für "ist-ein"-Beziehungen
Siehe auch: