Skip to content
IRC-Coding IRC-Coding
UML Polymorphie Dynamische Bindung Überschreiben Überladen Generics OOP Vererbung

UML Polymorphie Grundlagen: Dynamische Bindung & Überschreiben

UML Polymorphie mit dynamischer Bindung, Überschreiben, Überladen und Generics. Java Beispiele für polymorphe Beziehungen.

S

schutzgeist

2 min read

UML Polymorphie Grundlagen: Dynamische Bindung & Überschreiben

Polymorphie ist ein zentrales Konzept der objektorientierten Programmierung und UML-Modellierung. Sie ermöglicht es Objekten verschiedener Klassen, auf dieselbe Nachricht unterschiedlich zu reagieren.

Was ist Polymorphie?

Polymorphie (Vielgestaltigkeit) beschreibt die Fähigkeit von Objekten, dieselbe Schnittstelle zu verwenden, aber unterschiedliche Implementierungen zu haben. In UML wird dies durch Vererbungshierarchien und Schnittstellen dargestellt.

Arten der Polymorphie

  1. Überschreiben (Override): Methode in Unterklasse ersetzt Basisklassenmethode
  2. Überladen (Overload): Mehrere Methoden gleichen Namens mit unterschiedlichen Parametern
  3. Parametrische Polymorphie: Generics für typsichere Wiederverwendung
  4. Ad-hoc Polymorphie: Methodenüberladung und Typ-Konvertierung

UML-Darstellung von Polymorphie

Klassendiagramm mit Polymorphie

@startuml
abstract class Shape {
    -color: String
    -x: double
    -y: double
    +Shape(color: String, x: double, y: double)
    +move(dx: double, dy: double): void
    +area(): double {abstract}
    +perimeter(): double {abstract}
    +toString(): String
}

class Rectangle {
    -width: double
    -height: double
    +Rectangle(color: String, x: double, y: double, width: double, height: double)
    +area(): double
    +perimeter(): double
    +setDimensions(width: double, height: double): void
    +toString(): String
}

class Circle {
    -radius: double
    +Circle(color: String, x: double, y: double, radius: double)
    +area(): double
    +perimeter(): double
    +setRadius(radius: double): void
    +toString(): String
}

class Triangle {
    -base: double
    -height: double
    +Triangle(color: String, x: double, y: double, base: double, height: double)
    +area(): double
    +perimeter(): double
    +toString(): String
}

Shape <|-- Rectangle
Shape <|-- Circle
Shape <|-- Triangle

@enduml

Sequenzdiagramm für dynamische Bindung

@startuml
actor User
User -> ShapeProcessor: processShapes(shapes)
activate ShapeProcessor

loop für jede Form
    ShapeProcessor -> Shape: area()
    activate Shape
    
    alt Rectangle
        Shape --> ShapeProcessor: Rectangle.area()
    else Circle
        Shape --> ShapeProcessor: Circle.area()
    else Triangle
        Shape --> ShapeProcessor: Triangle.area()
    end
    
    deactivate Shape
    ShapeProcessor -> Shape: perimeter()
    activate Shape
    
    alt Rectangle
        Shape --> ShapeProcessor: Rectangle.perimeter()
    else Circle
        Shape --> ShapeProcessor: Circle.perimeter()
    else Triangle
        Shape --> ShapeProcessor: Triangle.perimeter()
    end
    
    deactivate Shape
end

ShapeProcessor --> User: Ergebnisse
deactivate ShapeProcessor
@enduml

Dynamische Bindung in Java

Überschreiben und dynamischer Dispatch

public class PolymorphismDemo {
    
    // Abstrakte Basisklasse
    public abstract class Shape {
        protected String color;
        protected double x, y;
        
        public Shape(String color, double x, double y) {
            this.color = color;
            this.x = x;
            this.y = y;
        }
        
        // Überschreibbare Methode
        public void move(double dx, double dy) {
            this.x += dx;
            this.y += dy;
            System.out.println(color + " Form verschoben nach (" + x + ", " + y + ")");
        }
        
        // Abstrakte Methoden - müssen überschrieben werden
        public abstract double area();
        public abstract double perimeter();
        
        // Konkrete Methode kann überschrieben werden
        public String getDescription() {
            return "Eine " + color + " Form an Position (" + x + ", " + y + ")";
        }
        
        // Getter
        public String getColor() { return color; }
        public double getX() { return x; }
        public double getY() { return y; }
    }
    
    // Rectangle - überschreibt abstrakte Methoden
    public class Rectangle extends Shape {
        private double width, height;
        
        public Rectangle(String color, double x, double y, double width, double height) {
            super(color, x, y);
            this.width = width;
            this.height = height;
        }
        
        @Override
        public double area() {
            return width * height;
        }
        
        @Override
        public double perimeter() {
            return 2 * (width + height);
        }
        
        @Override
        public String getDescription() {
            return super.getDescription() + " (Rechteck " + width + "x" + height + ")";
        }
        
        // Zusätzliche Methode
        public void setDimensions(double width, double height) {
            this.width = width;
            this.height = height;
        }
    }
    
    // Circle - überschreibt abstrakte Methoden
    public class Circle extends Shape {
        private double radius;
        
        public Circle(String color, double x, double y, double radius) {
            super(color, x, y);
            this.radius = radius;
        }
        
        @Override
        public double area() {
            return Math.PI * radius * radius;
        }
        
        @Override
        public double perimeter() {
            return 2 * Math.PI * radius;
        }
        
        @Override
        public String getDescription() {
            return super.getDescription() + " (Kreis mit Radius " + radius + ")";
        }
        
        public void setRadius(double radius) {
            this.radius = radius;
        }
    }
    
    // Dynamische Bindung Demonstration
    public void demonstrateDynamicBinding() {
        List<Shape> shapes = new ArrayList<>();
        shapes.add(new Rectangle("rot", 0, 0, 5, 3));
        shapes.add(new Circle("blau", 10, 10, 2));
        shapes.add(new Rectangle("grün", 5, 5, 2, 2));
        
        // Polymorphe Verarbeitung - dynamischer Dispatch
        for (Shape shape : shapes) {
            System.out.println(shape.getDescription());
            
            // Dynamische Bindung - je nach Objekttyp wird die richtige Methode aufgerufen
            double area = shape.area();        // Wählt Rectangle.area() oder Circle.area()
            double perimeter = shape.perimeter(); // Wählt Rectangle.perimeter() oder Circle.perimeter()
            
            System.out.println("  Fläche: " + String.format("%.2f", area));
            System.out.println("  Umfang: " + String.format("%.2f", perimeter));
            
            // Auch move() kann überschrieben werden
            shape.move(1, 1);
            System.out.println();
        }
    }
}

Methodenüberladung

Überladen in Java

public class MethodOverloading {
    
    // Überladene Methoden für unterschiedliche Parametertypen
    public class Calculator {
        
        // Überladen für int
        public int add(int a, int b) {
            System.out.println("int add(int, int) aufgerufen");
            return a + b;
        }
        
        // Überladen für double
        public double add(double a, double b) {
            System.out.println("double add(double, double) aufgerufen");
            return a + b;
        }
        
        // Überladen für drei Parameter
        public int add(int a, int b, int c) {
            System.out.println("int add(int, int, int) aufgerufen");
            return a + b + c;
        }
        
        // Überladen für Arrays
        public int add(int[] numbers) {
            System.out.println("int add(int[]) aufgerufen");
            int sum = 0;
            for (int num : numbers) {
                sum += num;
            }
            return sum;
        }
        
        // Überladen mit varargs
        public int addVarargs(int... numbers) {
            System.out.println("int addVarargs(int...) aufgerufen");
            return add(numbers);
        }
        
        // Überladen für unterschiedliche Objekttypen
        public String concatenate(String a, String b) {
            System.out.println("String concatenate(String, String) aufgerufen");
            return a + b;
        }
        
        public String concatenate(String a, String b, String c) {
            System.out.println("String concatenate(String, String, String) aufgerufen");
            return a + b + c;
        }
    }
    
    // Überladung Demonstration
    public void demonstrateOverloading() {
        Calculator calc = new Calculator();
        
        // Verschiedene Überladungen werden aufgerufen
        System.out.println("5 + 3 = " + calc.add(5, 3));
        System.out.println("5.5 + 3.3 = " + calc.add(5.5, 3.3));
        System.out.println("1 + 2 + 3 = " + calc.add(1, 2, 3));
        System.out.println("Array sum = " + calc.add(new int[]{1, 2, 3, 4, 5}));
        System.out.println("Varargs sum = " + calc.addVarargs(1, 2, 3, 4, 5));
        System.out.println("Hello + World = " + calc.concatenate("Hello", "World"));
        System.out.println("A + B + C = " + calc.concatenate("A", "B", "C"));
    }
}

Überladung mit Vererbung

public class OverloadingWithInheritance {
    
    public class Animal {
        public void makeSound() {
            System.out.println("Animal makes a sound");
        }
        
        public void makeSound(String intensity) {
            System.out.println("Animal makes a " + intensity + " sound");
        }
    }
    
    public class Dog extends Animal {
        @Override
        public void makeSound() {
            System.out.println("Dog barks");
        }
        
        // Überladen, nicht überschrieben
        public void makeSound(String intensity) {
            System.out.println("Dog barks " + intensity);
        }
        
        // Zusätzliche Überladung
        public void makeSound(String intensity, int times) {
            for (int i = 0; i < times; i++) {
                System.out.println("Dog barks " + intensity);
            }
        }
    }
    
    public void demonstrateOverloadingInheritance() {
        Animal animal = new Animal();
        Dog dog = new Dog();
        Animal animalDog = new Dog(); // Upcasting
        
        // Statische Bindung bei Überladung (Compile-Time)
        animal.makeSound();           // Animal makes a sound
        animal.makeSound("loud");     // Animal makes a loud sound
        
        dog.makeSound();              // Dog barks (überschrieben)
        dog.makeSound("loud");        // Dog barks loud (überladen)
        dog.makeSound("loud", 3);     // Dog barks loud (3x) (überladen)
        
        // Wichtig: Statische Bindung bei Überladung!
        animalDog.makeSound();        // Dog barks (dynamische Bindung)
        animalDog.makeSound("loud");  // Animal makes a loud sound (statische Bindung!)
    }
}

Generics und parametrische Polymorphie

Generic Klassen

public class GenericPolymorphism {
    
    // Generic Container Klasse
    public class Container<T> {
        private T content;
        private String label;
        
        public Container(String label, T content) {
            this.label = label;
            this.content = content;
        }
        
        public T getContent() {
            return content;
        }
        
        public void setContent(T content) {
            this.content = content;
        }
        
        public String getLabel() {
            return label;
        }
        
        // Generic Methode
        public <U> Container<U> transform(Function<T, U> transformer) {
            U newContent = transformer.apply(content);
            return new Container<>(label, newContent);
        }
        
        @Override
        public String toString() {
            return label + ": " + content;
        }
    }
    
    // Generic Processor
    public class Processor<T> {
        public List<T> filter(List<T> items, Predicate<T> predicate) {
            return items.stream()
                .filter(predicate)
                .collect(Collectors.toList());
        }
        
        public <R> List<R> map(List<T> items, Function<T, R> mapper) {
            return items.stream()
                .map(mapper)
                .collect(Collectors.toList());
        }
        
        public T reduce(List<T> items, BinaryOperator<T> accumulator, T identity) {
            return items.stream()
                .reduce(identity, accumulator);
        }
    }
    
    // Demonstration
    public void demonstrateGenerics() {
        // Container mit verschiedenen Typen
        Container<String> stringContainer = new Container<>("Text", "Hello World");
        Container<Integer> intContainer = new Container<>("Zahl", 42);
        Container<List<String>> listContainer = new Container<>("Liste", 
            Arrays.asList("A", "B", "C"));
        
        System.out.println(stringContainer);
        System.out.println(intContainer);
        System.out.println(listContainer);
        
        // Transformation mit Generic Methode
        Container<Integer> lengthContainer = stringContainer.transform(String::length);
        System.out.println("Länge: " + lengthContainer);
        
        // Generic Processor
        Processor<String> stringProcessor = new Processor<>();
        List<String> words = Arrays.asList("apple", "banana", "cherry", "date");
        
        // Filtern
        List<String> longWords = stringProcessor.filter(words, s -> s.length() > 5);
        System.out.println("Lange Wörter: " + longWords);
        
        // Mappen
        List<Integer> lengths = stringProcessor.map(words, String::length);
        System.out.println("Längen: " + lengths);
        
        // Reduzieren
        String concatenated = stringProcessor.reduce(words, String::concat, "");
        System.out.println("Zusammengefügt: " + concatenated);
    }
}

Generic Methoden

public class GenericMethods {
    
    // Generic Methode für Vergleich
    public static <T extends Comparable<T>> T max(T a, T b) {
        return a.compareTo(b) > 0 ? a : b;
    }
    
    // Generic Methode für Swap
    public static <T> void swap(T[] array, int i, int j) {
        T temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }
    
    // Generic Methode für Konvertierung
    public static <T, R> List<R> convertList(List<T> list, Function<T, R> converter) {
        return list.stream()
            .map(converter)
            .collect(Collectors.toList());
    }
    
    // Generic Methode mit wildcards
    public static void printList(List<?> list) {
        for (Object item : list) {
            System.out.println(item);
        }
    }
    
    // Upper Bounded Wildcard
    public static double sumOfNumbers(List<? extends Number> numbers) {
        return numbers.stream()
            .mapToDouble(Number::doubleValue)
            .sum();
    }
    
    // Lower Bounded Wildcard
    public static void addNumbers(List<? super Integer> list) {
        list.add(1);
        list.add(2);
        list.add(3);
    }
    
    // Demonstration
    public static void demonstrateGenericMethods() {
        // Max Methode
        System.out.println("Max von 5 und 3: " + max(5, 3));
        System.out.println("Max von 'Hello' und 'World': " + max("Hello", "World"));
        
        // Swap Methode
        String[] words = {"A", "B", "C"};
        System.out.println("Vor Swap: " + Arrays.toString(words));
        swap(words, 0, 2);
        System.out.println("Nach Swap: " + Arrays.toString(words));
        
        // Convert Methode
        List<String> strings = Arrays.asList("1", "2", "3", "4", "5");
        List<Integer> integers = convertList(strings, Integer::parseInt);
        System.out.println("Konvertiert: " + integers);
        
        // Wildcard Methoden
        List<String> stringList = Arrays.asList("A", "B", "C");
        List<Integer> intList = Arrays.asList(1, 2, 3);
        
        System.out.println("String Liste:");
        printList(stringList);
        
        System.out.println("Integer Liste:");
        printList(intList);
        
        // Upper bounded wildcard
        List<Double> doubles = Arrays.asList(1.1, 2.2, 3.3);
        System.out.println("Summe: " + sumOfNumbers(doubles));
        
        // Lower bounded wildcard
        List<Number> numbers = new ArrayList<>();
        addNumbers(numbers);
        System.out.println("Zahlen hinzugefügt: " + numbers);
    }
}

Interfaces und Polymorphie

Interface-basierte Polymorphie

public class InterfacePolymorphism {
    
    // Interface für polymorphes Verhalten
    public interface Drawable {
        void draw();
        double getArea();
        String getType();
    }
    
    public interface Movable {
        void move(double dx, double dy);
        void setPosition(double x, double y);
        double[] getPosition();
    }
    
    // Klasse implementiert mehrere Interfaces
    public class Circle implements Drawable, Movable {
        private double radius, x, y;
        
        public Circle(double radius, double x, double y) {
            this.radius = radius;
            this.x = x;
            this.y = y;
        }
        
        @Override
        public void draw() {
            System.out.println("Zeichne Kreis an (" + x + ", " + y + ") mit Radius " + radius);
        }
        
        @Override
        public double getArea() {
            return Math.PI * radius * radius;
        }
        
        @Override
        public String getType() {
            return "Kreis";
        }
        
        @Override
        public void move(double dx, double dy) {
            x += dx;
            y += dy;
            System.out.println("Kreis verschoben nach (" + x + ", " + y + ")");
        }
        
        @Override
        public void setPosition(double x, double y) {
            this.x = x;
            this.y = y;
        }
        
        @Override
        public double[] getPosition() {
            return new double[]{x, y};
        }
    }
    
    public class Rectangle implements Drawable, Movable {
        private double width, height, x, y;
        
        public Rectangle(double width, double height, double x, double y) {
            this.width = width;
            this.height = height;
            this.x = x;
            this.y = y;
        }
        
        @Override
        public void draw() {
            System.out.println("Zeichne Rechteck an (" + x + ", " + y + ") " + width + "x" + height);
        }
        
        @Override
        public double getArea() {
            return width * height;
        }
        
        @Override
        public String getType() {
            return "Rechteck";
        }
        
        @Override
        public void move(double dx, double dy) {
            x += dx;
            y += dy;
            System.out.println("Rechteck verschoben nach (" + x + ", " + y + ")");
        }
        
        @Override
        public void setPosition(double x, double y) {
            this.x = x;
            this.y = y;
        }
        
        @Override
        public double[] getPosition() {
            return new double[]{x, y};
        }
    }
    
    // Polymorphe Verarbeitung
    public void processShapes(List<Drawable> shapes) {
        for (Drawable shape : shapes) {
            shape.draw();
            System.out.println("Typ: " + shape.getType());
            System.out.println("Fläche: " + shape.getArea());
            System.out.println();
        }
    }
    
    public void moveShapes(List<Movable> movables, double dx, double dy) {
        for (Movable movable : movables) {
            movable.move(dx, dy);
        }
    }
    
    // Demonstration
    public void demonstrateInterfacePolymorphism() {
        List<Drawable> shapes = new ArrayList<>();
        List<Movable> movables = new ArrayList<>();
        
        Circle circle = new Circle(2.0, 0, 0);
        Rectangle rectangle = new Rectangle(3.0, 4.0, 5, 5);
        
        shapes.add(circle);
        shapes.add(rectangle);
        movables.add(circle);
        movables.add(rectangle);
        
        System.out.println("=== Formen zeichnen ===");
        processShapes(shapes);
        
        System.out.println("=== Formen verschieben ===");
        moveShapes(movables, 10, 10);
        
        System.out.println("=== Nach dem Verschieben ===");
        processShapes(shapes);
    }
}

UML-Notation für Polymorphie

Klassendiagramm-Konventionen

@startuml
' Polymorphe Beziehung in UML
abstract class PaymentProcessor {
    +processPayment(amount: double): boolean {abstract}
    +validatePayment(amount: double): boolean {abstract}
}

class CreditCardProcessor {
    +processPayment(amount: double): boolean
    +validatePayment(amount: double): boolean
    +validateCardNumber(number: String): boolean
}

class PayPalProcessor {
    +processPayment(amount: double): boolean
    +validatePayment(amount: double): boolean
    +validateEmail(email: String): boolean
}

class BankTransferProcessor {
    +processPayment(amount: double): boolean
    +validatePayment(amount: double): boolean
    +validateBankDetails(iban: String): boolean
}

PaymentProcessor <|-- CreditCardProcessor
PaymentProcessor <|-- PayPalProcessor
PaymentProcessor <|-- BankTransferProcessor

' Interface für polymorphe Operationen
interface Refundable {
    +processRefund(amount: double): boolean
    +getRefundStatus(): String
}

CreditCardProcessor ..|> Refundable
PayPalProcessor ..|> Refundable
BankTransferProcessor ..|> Refundable

@enduml

Sequenzdiagramm für polymorphe Aufrufe

@startuml
actor Customer
Customer -> PaymentSystem: makePayment(amount, method)
activate PaymentSystem

PaymentSystem -> PaymentProcessorFactory: createProcessor(method)
activate PaymentProcessorFactory
PaymentProcessorFactory --> PaymentSystem: processor
deactivate PaymentProcessorFactory

PaymentSystem -> PaymentProcessor: processPayment(amount)
activate PaymentProcessor

alt Credit Card
    PaymentProcessor -> CreditCardProcessor: processPayment(amount)
    CreditCardProcessor --> PaymentProcessor: success
else PayPal
    PaymentProcessor -> PayPalProcessor: processPayment(amount)
    PayPalProcessor --> PaymentProcessor: success
else Bank Transfer
    PaymentProcessor -> BankTransferProcessor: processPayment(amount)
    BankTransferProcessor --> PaymentProcessor: success
end

PaymentProcessor --> PaymentSystem: result
deactivate PaymentProcessor

PaymentSystem --> Customer: payment result
deactivate PaymentSystem
@enduml

Best Practices für Polymorphie

1. Liskov Substitution Principle

// Gut: Rectangle kann Shape überall ersetzen
public class GoodPolymorphism {
    
    public interface Shape {
        double area();
        double perimeter();
        void move(double dx, double dy);
    }
    
    public class Rectangle implements Shape {
        private double width, height, x, y;
        
        public Rectangle(double width, double height, double x, double y) {
            this.width = width;
            this.height = height;
            this.x = x;
            this.y = y;
        }
        
        @Override
        public double area() {
            return width * height;
        }
        
        @Override
        public double perimeter() {
            return 2 * (width + height);
        }
        
        @Override
        public void move(double dx, double dy) {
            x += dx;
            y += dy;
        }
        
        // Zusätzliche Methoden verletzen LSP nicht
        public double getWidth() { return width; }
        public double getHeight() { return height; }
    }
    
    public class Square implements Shape {
        private double side, x, y;
        
        public Square(double side, double x, double y) {
            this.side = side;
            this.x = x;
            this.y = y;
        }
        
        @Override
        public double area() {
            return side * side;
        }
        
        @Override
        public double perimeter() {
            return 4 * side;
        }
        
        @Override
        public void move(double dx, double dy) {
            x += dx;
            y += dy;
        }
        
        public double getSide() { return side; }
    }
}

2. Interface Segregation

// Schlecht: Zu großes Interface
public interface BadShape {
    double area();
    double perimeter();
    void move(double dx, double dy);
    void rotate(double angle);
    void resize(double factor);
    Color getColor();
    void setColor(Color color);
}

// Gut: Spezialisierte Interfaces
public interface Drawable {
    void draw(Graphics g);
}

public interface Movable {
    void move(double dx, double dy);
    void setPosition(double x, double y);
    double[] getPosition();
}

public interface Resizable {
    void resize(double factor);
    void setSize(double width, double height);
}

public interface Rotatable {
    void rotate(double angle);
    double getRotation();
}

public interface Colored {
    Color getColor();
    void setColor(Color color);
}

// Klasse implementiert nur benötigte Interfaces
public class Circle implements Drawable, Movable, Resizable, Colored {
    // Implementierung...
}

3. Template Method Pattern

public abstract class DataProcessor {
    
    // Template Method - definiert Algorithmus
    public final void processData() {
        loadData();
        if (validateData()) {
            transformData();
            saveData();
            onSuccess();
        } else {
            onError();
        }
        cleanup();
    }
    
    // Abstrakte Methoden - müssen implementiert werden
    protected abstract void loadData();
    protected abstract boolean validateData();
    protected abstract void transformData();
    protected abstract void saveData();
    
    // Hook Methoden - können überschrieben werden
    protected void onSuccess() {
        System.out.println("Verarbeitung erfolgreich");
    }
    
    protected void onError() {
        System.out.println("Verarbeitung fehlgeschlagen");
    }
    
    protected void cleanup() {
        System.out.println("Aufräumen");
    }
}

public class CSVProcessor extends DataProcessor {
    @Override
    protected void loadData() {
        System.out.println("Lade CSV Daten");
    }
    
    @Override
    protected boolean validateData() {
        System.out.println("Validiere CSV Daten");
        return true;
    }
    
    @Override
    protected void transformData() {
        System.out.println("Transformiere CSV Daten");
    }
    
    @Override
    protected void saveData() {
        System.out.println("Speichere CSV Daten");
    }
    
    @Override
    protected void onSuccess() {
        System.out.println("CSV Verarbeitung erfolgreich!");
    }
}

Prüfungsrelevante Konzepte

Wichtige Unterscheidungen

  1. Überschreiben vs Überladen

    • Überschreiben: Gleiche Signatur in Unterklasse
    • Überladen: Gleicher Name, unterschiedliche Parameter
  2. Statische vs Dynamische Bindung

    • Statisch: Überladung (Compile-Time)
    • Dynamisch: Überschreiben (Runtime)
  3. Abstrakte Klasse vs Interface

    • Abstrakte Klasse: Gemeinsame Implementierung
    • Interface: Reiner Vertrag
  4. Generics vs Vererbung

    • Generics: Typsicherheit bei Compile-Time
    • Vererbung: Polymorphie bei Runtime

Typische Prüfungsaufgaben

  1. Zeichnen Sie UML-Diagramme für polymorphe Beziehungen
  2. Implementieren Sie überschriebene Methoden
  3. Erklären Sie dynamische Bindung
  4. Vergleichen Sie verschiedene Polymorphie-Arten
  5. Entwerfen Sie polymorphe Klassenhierarchien

Zusammenfassung

Polymorphie ist ein mächtiges Konzept für flexible Softwarearchitektur:

  • Überschreiben ermöglicht dynamische Bindung und Runtime-Polymorphie
  • Überladen bietet statische Bindung und Compile-Time-Polymorphie
  • Generics ermöglichen typsichere Wiederverwendung
  • Interfaces definieren polymorphe Verträge

Gute Polymorphie erfordert Einhaltung des Liskov Substitution Principles und sorgfältiges Interface Design für wartbare und erweiterbare Software.

Zurück zum Blog
Share:

Ähnliche Beiträge