Java, Generics

Generisches Programmieren


Generische Datentypen

Jede Klasse ist Unterklasse von Object.

Datentypen, die ganz allgemein über (Basis-)Elementen vom Typ Object definiert sind, heissen generisch.

Problem: bei der Verarbeitung muss ggf. zur Laufzeit mit instanceof geprüft werden welchen Datentyp die Objekte tatsächlich haben und mit einem entsprechenden Type-Cast angepasst werden.

Zum Beispiel die Klassen in java.util oder die folgende Klasse List. Diese Klasse implementiert Listen ähnlich wie in Scheme/Lisp.

public class List {

    private Object a; // allgemeine Daten
    private List d;   // Referenz auf das nächste ELement

    /**
     * Der Konstruktor und alle Objekt-Methoden sind nicht 
     * öffentlich.
     */
    protected List(Object ap, List dp) {
	a = ap; d = dp; 
    }

    protected Object car() { return a; }
    protected List cdr() { return d; }


    /**
     * Nur die Klassen-Methoden sind öffentlich.
     */
    public static List cons(Object a, List d) {
	return new List(a,d);
    }

    public static Object car(List n) {
	//if ( n == null ) return null;
	return n.car();
    }

    public static List cdr(List n) {
	//if ( n == null ) return null;
	return n.cdr();
    }

    /**
     * toString ist auch öffentlich.
     */
    public String toString() {
	String s = "(";
	List t = this;
	Object a;
	for (;;) { 
            a = t.car();
	    if ( a == null ) s += "null";
	    else s += a.toString();
	    if ( t.cdr() != null ) {
               s += " ";
               t = t.cdr();
            } else break;
	}
	s += ")";
	return s;
    }
...
}

Anwendung:

        List l = List.cons(null,null);
	l = List.cons(l,l);
	l = List.cons(l,l);
	System.out.println("l = " + l);

	List x = new List(new Integer(2),null);
	System.out.println("x = " + x);

	List y = new List(new Double(3.0),null);
	System.out.println("y = " + y);

	y = List.cons( new Integer(4), y);
	System.out.println("y = " + y);

	List z = null;
	for (int i = 0; i < 10; i++) {
	    z = List.cons( new Integer(i), z);
	}
	System.out.println("z = " + z);

Ausgabe:

l = (((null) null) (null) null)
x = (2)
y = (3.0)
y = (4 3.0)
z = (9 8 7 6 5 4 3 2 1 0)

Datentypen als Parameter

Die Verwendung des allgemeinsten Datentypes Object hat den Nachteil, dass immer Typecasts zu den tatsächlich verwendeten Datentypen notwendig sind.

Wird die Sprache so erweitert, dass Datentypen als Parameter möglich sind, können die Programme unter Verwendung Datentyp-Parameter formuliert werden. Bei der Instanziierung von Objekten wird für den Typparameter ein konkreter Datentyp eingesetzt. Die nachträglichen Typecasts entfallen damit.

Für Datentypen als Parameter wird eine spezielle Syntax verwendet. Der Name des formalen Typparameters wird bei der Definition in spitze Klammern eingeschlossen:

public class A<Typname> { ... }
private Typname variable;
public Typname methodname( Typname a, ...) { ... }

In C++ sind Datentyp Parameter durch Klassen-Templates realisiert. Templates wie in C++ waren in Java bis JDK 1.4 nicht vorgesehen, sind aber ab JDK 1.5 vorhanden.

Neue Frage: Wie können Einschränkungen für die formalen Typparameter formuliert werden?

public class A<Typname extends Typ> { }

Beispiel

List Beispiel von oben als typsichere Liste.

public class GenList<Typname> implements Cloneable {

    private Typname a; // allgemeine Daten
    private GenList<Typname> d;   // Referenz auf das naechste ELement

    /**
     * Der Konstruktor und alle Objekt-Methoden sind nicht 
     * oeffentlich.
     */
    protected GenList(Typname ap, GenList<Typname> dp) {
	a = ap; d = dp; 
    }

    protected Typname car() { return a; }
    protected GenList<Typname> cdr() { return d; }

    protected void setcar(Typname ap) { a = ap; }
    protected void setcdr(GenList<Typname> dp) { d = dp; }


    /**
     * Nur die Klassen-Methoden sind oeffentlich.
     */
    public static <Typname> GenList<Typname> cons(Typname a, GenList<Typname> d) {
	return new GenList<Typname>(a,d);
    }

    public static <Typname> Typname car(GenList<Typname> n) {
	//if ( n == null ) return null;
	return n.car();
    }

    public static <Typname> GenList<Typname> cdr(GenList<Typname> n) {
	//if ( n == null ) return null;
	return n.cdr();
    }

    public static <Typname> void setcar(GenList<Typname> n, Typname a) {
	//if ( n == null ) return;
	n.setcar(a);
    }

    public static <Typname> void setcdr(GenList<Typname> n, GenList<Typname> d) {
	//if ( n == null ) return;
	n.setcdr(d);
    }

    public String toString() {
	String s = "(";
	GenList<Typname> t = this;
	Typname a;
	for (;;) { 
            a = t.car();
	    if ( a == null ) s += "null";
	    else s += a.toString();
	    if ( t.cdr() != null ) {
               s += " ";
               t = t.cdr();
            } else break;
	}
	s += ")";
	return s;
    }
...
}

Anwendung:

        GenList l = GenList.cons(null,null);
        System.out.println("l = " + l);
        l = GenList.cons(l,l);
        System.out.println("l = " + l);
        l = GenList.cons(l,l);
        System.out.println("l = " + l);

        GenList<Integer> x = new GenList(new Integer(2),null);
        System.out.println("x = " + x);

        GenList<Double> y = new GenList(new Double(3),null);
        System.out.println("y = " + y);

        // y = GenList.<Double>cons( new Integer(4), y); // error
        // System.out.println("y = " + y);

        GenList<Integer> z = null;
        for (int i = 0; i < 10; i++) {
            z = GenList.<Integer>cons( new Integer(i), z);
        }
        System.out.println("z = " + z);

        GenList<Integer> w = GenList.<Integer>append( z, z );
        System.out.println("w = " + w);

        Integer a = GenList.<Integer>ncar(z, 9);
        System.out.println("a = " + a);

        int len = GenList.length(w);
        System.out.println("len = " + len);

        GenList<Integer> u = GenList.<Integer>reverse( w );
        System.out.println("u = " + u);

        System.out.println("reverse(l) = " + GenList.reverse(l));

        GenList<Integer> v = (GenList<Integer>)GenList.<Integer>clone(l);
        System.out.println("clone(l) = " + v);

        System.out.println("equals(v,l) = " + GenList.equals(v,l));
        System.out.println("equals(w,u) = " + GenList.equals(w,u));

Ausgabe:

l = (null)
l = ((null) null)
l = (((null) null) (null) null)
x = (2)
y = (3.0)
z = (9 8 7 6 5 4 3 2 1 0)
w = (9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0)
a = 0
len = 20
u = (0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9)
reverse(l) = (null (null) ((null) null))
clone(l) = (((null) null) (null) null)
equals(v,l) = true
equals(w,u) = false

Neuerungen in der Java Sprache ab JDK 1.5:


© Universität Mannheim, Rechenzentrum, 2002-2008.

Heinz Kredel

Last modified: Sun Oct 5 15:21:10 CEST 2008