Tutorium 15.04

Wiederholung

Interface vs. Abstrakte Klassen
 1 interface Flyable {
 2   void fly();
 3 }
 4
 5 abstract class Animal {
 6   String name;
 7
 8   Animal(String name) {
 9      this.name = name;
10   }
11
12   void eat() {
13      System.out.println(name + " eats");
14   }
15 }
16
17 class Bird extends Animal implements Flyable {
18   Bird(String name) {
19      super(name);
20   }
21
22   @Override
23   public void fly() {
24      System.out.println(name + " flies");
25   }
26 }

Aufgabe 1

Abstrakte Klasse oder Interface
1??? class DatabaseConnection {
2   void connect() { ... }
3}
Lösung anzeigen
  • Wenn man verschiedene Datenbanksysteme (MySQL, Postgres..) austauschbar machen möchte -> Interface

  • Wenn man beispielsweise eine ähnliche Verbindungslogik teilen -> abstrakte Klasse

Aufgabe 2

Es gibt 3 Java-Objekte

Nutzer
Rechnung
Bild

Alle sollten speicherbar sein - interface oder abstrakte Klasse?

Lösung anzeigen

Interface - Die Klassen haben keine natürliche Beziehung. Ein Bild ist keine Rechnung, sie teilen lediglich eine Fähigkeit.

Aufgabe 3

Wir haben Hunde-, Katzen- und Vogel-Objekte. Alle Tiere haben eine Namen, sowie ein Alter, des Weiteren können sie essen und lauteMachen.

Wie sollte man das am besten abbilden - Abstrakte Klasse oder Interface?

Lösung anzeigen
 1abstract class Animal {
 2   String name;
 3   int age; // Instanzvariablen
 4
 5   Animal(String name, int age) {
 6      this.name = name;
 7      this.age = age;
 8   }
 9
10   void eat() {
11      System.out.println(name + " is eating");
12   }
13
14   abstract void makeSound();
15}

Aufgabe 4

Wir wollen einen einfachen Rechner mit methoden wie addieren() und subtrahieren() bauen. Wie würden wir das am besten Umsetzen?

Lösung anzeigen
1 class Calculator {
2     int add(int a, int b) {
3         return a + b;
4     }
5 }

Aufgabe 5

Wir haben 3 Objekte Bericht, Bild und Ticket, alle sollen über die Funktionalität drucken verfügen.

Lösung anzeigen
1interface Druckbares {
2     void print();
3}

Klassen haben keinen Bezug zueinander, wieder nur geteilte Funktionalität.

Aufgabe 6

Wir haben eine Firma mit drei verschiedenen Angestellten: Vollzeitangestellter, Halbtagsangestellter und Freelancer. Alle haben einen Namen und eine AngestelltenID - aber eine unterschiedliche Berechnung bezüglich des Lohns.

Lösung anzeigen
 1 abstract class Employee {
 2     String name;
 3     int employeeId;
 4
 5     Employee(String name, int employeeId) {
 6         this.name = name;
 7         this.employeeId = employeeId;
 8     }
 9
10     abstract double calculateSalary();
11 }

Aufgabe 7

Wir haben eine Ente, eine FliegendeFischDrone und einen Rettungsroboter - ein paar können fliegen, ein paar schwimmen und manche können auch beides.

Lösung anzeigen
1 interface Flyable {
2     void fly();
3 }
4
5 interface Swimmable {
6     void swim();
7 }

Es sind Fertigkeiten und eine Klasse kann mehrere interfaces implementieren aber nicht mehrere Klassen erben.

1class Duck implements Flyable, Swimmable {
2    public void fly() {}
3    public void swim() {}
4}

Aufgabe 8

Wir haben ein Auto, einen Truck und ein Motorrad. Alle haben eine maximale Geschwindigkeit sowie eine Tankgröße sowie die Funktionalität einer Beschleunigung, die für alle Gefährte gleich ist.

Lösung anzeigen
 1 abstract class Vehicle {
 2     int speed;
 3     int fuel;
 4
 5     void accelerate() {
 6         speed += 10;
 7     }
 8
 9     abstract void move();
10 }

Geteilte Logik, Felder und Bezug zueinander unter den Objekten.

Aufgabe 9

Wir haben drei Objekte: EmailNotification, SmsNotification, PushNotification, und alle sollten die Funktionalität send(String message) verfügen.

Lösung anzeigen
1 interface NotificationSender {
2     void send(String message);
3 }
4
5 default boolean Method() {System.out.println()}

Aufgabe 10

Wir haben einen Kreis, ein Rechteck und ein Dreieck. Alle haben eine Farbe und optional die Funktion sich in x oder y Richtung zu verschieben. Die Flächenberechnung unterscheidet sich natürlich je nach Form.

Lösung anzeigen
1 abstract class Shape {
2     String color;
3
4     Shape(String color) {
5         this.color = color;
6     }
7
8     abstract double area();
9 }

Array-Sortierung

Arrays können in Java ganz schnell wie z.B.

java.util.Arrays.sort(zahlen);
java.util.Arrays.sort(stringListe);

sortiert werden.

Comparable-Interface

Im Hintergrund funktioniert das über das Interface Comparable. Implementiert man dieses

 1 public class Ring implements Comparable <Ring> {
 2
 3     private double durchmesser;
 4
 5     public Ring(double durchmesser) {
 6         this durchmesser = durchmesser;
 7     }
 8
 9     public int compareTo(Ring o) {
10         double durchmesser_tmp = o.durchmesser
11         if (durchmesser < durchmesser_tmp) return -1;
12         if (durchmesser > durchmesser_tmp return 1;
13         else return 0;
14     }
15
16     public String toString() {
17         return "Ring der Größe " + durchmesser;
18     }
19
20 }

Generizität (Generics)

In dem Beispiel haben wir zwei Objekte: Ohrring und Socke.

1 public class Ohrring {
2     public String toString() {
3         return "Ohrring";
4     }
5 }
1 public class Socke {
2     public String toString() {
3         return "Socke";
4     }
5 }

Jetzt wollen wir die Socken zusammenlagen, bzw. Paare bilden damit alles zusammenpasst. Da wir nicht Klassen für OhrringPaar und für SockenPaar schreiben wollen müssen wir generalisieren und verwenden als Paar-Typ ein Object.

 1 public class Paar {
 2
 3     private Object links, rechts;
 4
 5     public Paar(Object links, Object rechts) {
 6         this.links = links;
 7         this.rechts = rechts;
 8     }
 9
10     public Object getLinks() {
11         return this.links;
12     }
13
14     public String toString() {
15         return "Paar: " + "(" + links + " und " + rechts + ")";
16     }
17
18 }

Damit haben wir eine allgemeine Generizität, die jedoch bei der Kompilierung unbekannt ist und zu Fehlern führen kann, da…

1public static void main(String[] args) {
2    Socke socke = new Socke();
3    Ohrring ohrring = new Ohrring();
4
5    Paar paar = new Paar(socke, ohrring);
6
7    print(paar);
8}

Type-Parameter (Platzhaltervariablen)

 1 public class Paar<T> {
 2
 3     private T links, rechts;
 4
 5     public Paar(T links, T rechts) {
 6         this.links = links;
 7         this.rechts = rechts;
 8     }
 9
10     public T getLinks() {
11         return this.links;
12     }
13
14     public String toString() {
15         return "Paar: " + "(" + links + " und " + rechts + ")";
16     }
17 }
1 public static void main(String[] args) {
2     Socke s1 = new Socke();
3     Socke s2 = new Socke();
4
5     Paar<Socke> sockenPaar = new Paar<Socke>(s1, s2);
6 }

Untergruppen

image shows inheritance example for visual understanding
extends
public class Paar<T extends Schuhe> { ... }
image shows inheritance example for visual understanding with extends Type-Parameter.
wildcards
List<?> dinge;
Bound wildcards
List<? extends Schuhe>;

List<? super Schuhe>;
image shows inheritance example for visual understanding with super Type-Parameter.

Methoden

Wieder aus unserem Paar beispiel..

public static <T> boolean linksGleichRechts(Paar<T> x) {
    return x.getLinks().equals(x.getRight());
}

public static void main(String[] args) {
    Hose hose = new Hose();
    Jeans jeans = new Jeans();

    Paar<Hose> paar1 = new Paar<Hose>(hose, jeans);
    System.out.println(linksGleichRechts(paar));
}

Wichtig

🚩 Was passiert, wenn man <T> durch eine Wildcard ersetzt <?>?

Lösung anzeigen
1 public static <T> T getLeft(Paar<T> x) {
2     return x.getLinks();
3 }
1 public static ??? getLeft(Paar<?> x) // unmöglich
Nimm <?> wenn der Typ egal ist - <T> wenn der Typ nicht egal ist!
public static <T> boolean sameLeftType(Paar<T> a, Paar<T> b)

public static boolean irgendwas(Paar<?> a, Paar<?> b)
1public static <T,S extends T> GenPaar<T> linksPaar(Paar<T> x, Paar<S> y) {
2    return new GenPaar<T>(x.getL(),y.getL());
3}
Wie kann diese Methode verwendet werden?

Übungen

Übung 1

Funktioniert das so?
1Paar<?> p = new Paar<String>("a", "b");
2String s = p.getLinks();
Lösung anzeigen

Das funktioniert nicht, da der Typ des Paars Paar<?> nicht definiert ist.

Übung 2

Funktioniert das?
1List<String> temp = new ArrayList<>();
2List<?> list = temp;
3
4temp.add("Hallo");
5list.add("Test");
Lösung anzeigen
temp.add("Hallo") funktioniert
list.add("Test") funktioniert nicht, da nur Sicht auf die Liste aber Typ unbekannt (nicht veränderbar)

Übung 3

Welches Problem könnte hierbei entstehen und welchen Vorteil hätte eine generische Klasse Box<T>?
 1class Box {
 2    private Object wert;
 3
 4    public Box(Object wert) {
 5        this.wert = wert;
 6    }
 7
 8    public Object getWert() {
 9        return wert;
10    }
11}
Box b = new Box("Hallo");
String s = (String) b.getWert();
Lösung anzeigen

Ohne Generics liefert getWert() nur Object. Deshalb braucht man einen Cast. Dabei kann ein Laufzeitfehler entstehen, wenn der Inhalt nicht zum Cast passt. Mit Box<T> kennt der Compiler den Typ schon vorher und viele Fehler werden bereits beim Kompilieren erkannt.

Übung 4

In eigenen Worten - was bedeutet <T> und warum sind in Paar<Integer> nur Integer-Werte und in Paar<String> nur String-Werte erlaubt?

Was bedeutet <T> in einer Klasse?
public class Paar<T> {
    private T links;
    private T rechts;

    public Paar(T links, T rechts) {
        this.links = links;
        this.rechts = rechts;
    }

    public T getLinks() {
        return links;
    }
}
Paar<Integer> p1 = new Paar<>(1, 2);
Paar<String> p2 = new Paar<>("A", "B");
Lösung anzeigen

<T> ist ein Platzhalter für einen Typ. Erst beim Erzeugen des Objekts wird festgelegt, welcher konkrete Typ gemeint ist. Bei Paar<Integer> ist T also Integer, bei Paar<String> ist T gleich String.

Übung 5

Warum braucht eine Methode manchmal ein eigenes <T>?
public static <T> T gibLinksZurueck(Paar<T> p) {
    return p.getLinks();
}
Was würde fehlen oder nicht mehr funktionieren, wenn man nur schreiben würde?
public static T gibLinksZurueck(Paar<T> p)
Lösung anzeigen

Das <T> vor dem Rückgabetyp führt den Typparameter für diese Methode ein. Ohne dieses <T> kennt der Compiler den Namen T an dieser Stelle gar nicht. Die Schreibweise

public static T ...

geht deshalb nicht, wenn T nicht vorher definiert wurde.

Übung 6

Was kompiliert, was nicht?
1List<String> a = new ArrayList<String>();
2List<String> b = new ArrayList<>();
3List<Object> c = new ArrayList<String>();
4List<?> d = new ArrayList<String>();
5Object e = d.get(0);
6String f = d.get(0);
7String g = (String) d.get(0);
Lösung anzeigen
1List<String> a = new ArrayList<String>();   // kompiliert
2List<String> b = new ArrayList<>();         // kompiliert
3List<Object> c = new ArrayList<String>();   // kompiliert nicht
4List<?> d = new ArrayList<String>();        // kompiliert
5Object e = d.get(0);                        // kompiliert
6String f = d.get(0);                        // kompiliert nicht
7String g = (String) d.get(0);               // kompiliert

Wichtig

  • List<String> ist nicht automatisch eine List<Object>

  • bei List<?> ist der Inhalt nur sicher als Object lesbar

  • ein Cast ist erlaubt, aber riskant

Übung 7

Wozu braucht man eingeschränkte Typenparameter?
1public static <T extends Number> double doppelt(T wert) {
2    return wert.doubleValue() * 2;
3}
Was wäre das Problem, wenn die Methode nur so geschrieben würde?
1public static <T> double doppelt(T wert)
Lösung anzeigen

Die Methode benutzt doubleValue(). Diese Methode gibt es bei Number und ihren Unterklassen, aber nicht bei beliebigen Typen. Ohne extends Number könnte auch ein String übergeben werden, und dann wäre doubleValue() nicht vorhanden.