Lösungen, Antworten

1. Antwort: Instanziierungen

Gegeben seien die folgenden Klassen:

public class Kaefig<T> {
    private T einTier;
    public void setTier(T x) {
        einTier = x;
    }
    public T getTier() {
        return einTier;
    }
 
   public class Tier{ }
   public class Hund extends Tier { }
   public class Vogel extends Tier { }
}

Beschreiben Sie was mit dem folgenden Code geschieht. Die Möglichkeiten sind

  • er übersetzt nicht
  • er übersetzt mit Warnungen
  • er erzeugt Fehler während der Ausführung
  • er übersetzt und läuft ohne Probleme

1.1

Kaefig<Tier> zwinger = new Kaefig<Hund>();

Übersetzungsfehler: Hund ist zwar eine Unterklasse von Tier. Kaefig<Tier> und Kaefig<Hund> sind keine kompatiblen Typen.

1.2

Kaefig<Vogel> voliere = new Kaefig<Tier>();

Übersetzungsfehler: Vogel ist zwar eine Unterklasse von Tier. Kaefig<Tier> und Kaefig<Vogel> sind keine kompatiblen Typen.

1.3

Kaefig<?> voliere = new Kaefig<Vogel>();
voliere.setTier(new Vogel());

Übersetzungsfehler in der zweiten Zeile. Die erste Zeile ist in Ordnung. Man kann einen Kaefig eines unbekannten aktuellen Parametertyps erzeugen. Die zweite Zeile kann nicht übersetzen, da der Übersetzer nicht wissen kann welche Tiere in voliere verwaltet werden sollen. Der folgende Code würde übersetzen:

Kaefig<?> voliere = new Kaefig<Vogel>();
Kaefig<Vogel> k= new Kaefig<Vogel>();
k.setTier(new Vogel());
voliere=k;

Anmerkung: Man darf nur Referenzen auf Zeiger mit Wildcards (hier voliere) zuweisen. Man darf keine Objektmethoden aufrufen weil hierfür der Typ zur Übersetzungszeit nicht bekannt ist.

1.4

Kaefig voliere = new Kaefig();
voliere.setTier(new Vogel());

Der Übersetzer übersetzt den Quellcode mit einer Warnung. Er kann nicht wissen welchen Typ er benutzt. Er erzeugt deshalb eine Warnung, weil es beim Zuweisen eines Vogels zu Problemen kommen kann. Man verliert die Vorteile der Typprüfung der generischen Klassen. Dieser Codierstil sollte deshalb vermieden werden.

2. Umwandeln einer nicht generischen Implementierung in eine generische Implementierung

package s2.generics;

/**
*
* @author s@scalingbits.com
* @param <T> ein Getraenk
*/
public class FlascheGeneric<T extends Getraenk> {

   T inhalt = null;

   public boolean istLeer() {
      return (inhalt == null);
   }

   public void fuellen(T g) {
      inhalt = g;
   }

   public T leeren() {
      T result = inhalt;
      inhalt = null;
   return result;
   }

   public static void main(String[] varargs) {
   // in generischer Implementierung soll
   // f1 nur für Bier dienen
      FlascheGeneric<Bier> f1 = new FlascheGeneric<Bier>();
      f1.fuellen(new Bier("DHBW-Bräu"));
      System.out.println("f1 geleert mit " + f1.leeren());

      f1 = new FlascheGeneric<Bier>();
      f1.fuellen(new Bier("DHBW-Export"));
      System.out.println("f1 geleert mit " + f1.leeren());
      // In der generischen Implementierung soll f2 nur für
      // Weinflaschen dienen
      FlascheGeneric<Wein> f2;
      f2 = new FlascheGeneric<Wein>();
      f2.fuellen(new Weisswein("Pfalz"));
      System.out.println("f2 geleert mit " + f2.leeren());

      f2 = new FlascheGeneric<Wein>();
      f2.fuellen(new Rotwein("Bordeaux"));
      System.out.println("f2 geleert mit " + f2.leeren());
   }
}

3. Typprüfungen

Welche Zeilen in der main() Methoder werden vom Übersetzer nicht übersetzt? 

Markieren Sie die Zeilen und nennen Sie den Fehler:

package s2.generics;

public class KoordinateTest<T extends Number> {

   public T x;
   public T y;

   public KoordinateTest(T xp, T yp) {
      x = xp;
     y = yp;
   }

   public static void main(String[] args) {
      KoordinateTest<Double> k11, k12;
      KoordinateTest<Integer> k21, k22;
      //Koordinate<String> k31; Die Klasse String ist nicht aus Number abgeleitet. Siehe extends Klausel
      KoordinateTest<Number> k41, k42;
      //k11 = new KoordinateTest<Float>(2.2d, 3.3d); Die Eingabeparameter sind vom Typ double und nicht vom Typ Float
      k12 = new KoordinateTest<Double>(2.2d, 3.3d);
      k21 = new KoordinateTest<Integer>(2, 3);
      //k31 = new Koordinate<String>("11","22"); Nicht erlaubt, da der Typ weiter oben nicht für die Variable erlaubt war
      k41 = new KoordinateTest<Number>(2l, 3l);
      k41 = new KoordinateTest<Number>(4.4d, 5.5f);
      //k11 = new Koordinate<Double>(3.3f,9.9d); Der erste Parameter ist ein Float und nicht ein Double wie gefordert

      KoordinateTest<? super Double> k99;
      //k99 = k11; Nicht erlaubt, da der Typ weiter oben nicht für die Variable erlaubt war 
      k99 = k41;
      //k99 = k31; Nicht erlaubt, da der Typ weiter oben nicht für die Variable erlaubt war

      k11 = k12;
      //k12 = k21; k21 ist vom Typ KoordinateTest<Integer>. k12 muss aber vom Typ KoordinateTest<Double> sein
      KoordinateTest k55 = new KoordinateTest<Number>(7.7f, 8.8f);
      KoordinateTest k66 = new KoordinateTest(7.7f, 8.8f);
   }
}