Funktionen höherer Ordnung
Generische Datentypen
Datentypen als Parameter
In der Mathematik Summe_{i=1,n} f(i) oder auch Produkt_{i=1,n} f(i)
Zum Beispiel die Lisp-Funktion summe
:
(define (summe f a b) (if (> a b) 0 (+ (f a) (summe f (+ a 1) b) ) ) ) (define (id x) x) (define (sqr x) (* x x)) (summe id 1 4) 10 (summe sqr 1 4) 30
Mit einem Interface und
public interface SummenFunktion { public double f(int i); }
public class Summation { double summe( SummenFunktion sf, int a, int b ) { double sum = 0.0; for (int i = a; i <= b; i++) sum += sf.f(i); return sum; } }
Summation UML Diagramm
public class Identitaet implements SummenFunktion { public double f(int i) { return (double) i; } } public class Quadrat implements SummenFunktion { public double f(int i) { double d = (double) i; return d*d; } }
public class SummenTest { public static void main(String[] args) { Screen out = new Screen(); Summation s = new Summation(); SummenFunktion sf = null; double sum = 0.0; sf = new Identitaet(); sum = s.summe( sf, 1, 4); out.println("Summe von 0 bis ? von f(i) = i ist " + sum); sf = new Quadrat(); sum = s.summe( sf, 1, 4); out.println("Summe von 0 bis ? von f(i) = i*i ist " + sum); } }
Zum Beispiel die Lisp-Funktion operation
:
(define (operation op init f a b) (if (> a b) init (op (f a) (operation op init f (+ a 1) b) ) ) ) (operation + 0 id 1 4) 10 (operation + 0 sqr 1 4) 30 (operation * 1 id 1 4) 24 (operation * 1 sqr 1 4) 576
public interface Operation { public double getInit(); public double operation( double x, double y); }
Operation UML Diagramm
public class OperSum implements Operation { public double getInit() { return 0.0; } public double operation( double x, double y) { return x + y; } }
public class OperMult implements Operation { public double getInit() { return 1.0; } public double operation( double x, double y) { return x * y; } }
public interface OperationFunktion { public double f(int i); }
public class GeneralOperation { Operation oper = null; public GeneralOperation( Operation op ) { oper = op; } public double applyoperation( OperationFunktion of, int a, int b ) { double res = oper.getInit(); for (int i = a; i <= b; i++) res = oper.operation( res, of.f(i)); return res; } }
public class OperationTest { public static void main(String[] args) { Screen out = new Screen(); OperationFunktion of = null; double erg = 0.0; Operation sum = new OperSum(); GeneralOperation summe = new GeneralOperation( sum ); of = new Identitaet(); erg = summe.applyoperation( of, 1, 4); out.println("Summe von 0 bis ? von f(i) = i ist " + erg); of = new Quadrat(); erg = summe.applyoperation( of, 1, 4); out.println("Summe von 0 bis ? von f(i) = i*i ist " + erg); Operation prod = new OperMult(); GeneralOperation product = new GeneralOperation( prod ); of = new Identitaet(); erg = product.applyoperation( of, 1, 4); out.println("Produkt von 0 bis ? von f(i) = i ist " + erg); of = new Quadrat(); erg = product.applyoperation( of, 1, 4); out.println("Produkt von 0 bis ? von f(i) = i*i ist " + erg); } }
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; } ... }
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);
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)
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 meist 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++ sind in Java bis JDK 1.4 nicht vorgesehen, aber voraussichtlich ab JDK 1.5 wird es das geben.
Neue Frage: Wie können Einschränkungen für die formalen Typparameter formuliert werden?
public class A<Typname implementing Interfaces> { }
Ein Beispiel in GJ:
public class List<EinTyp> { private EinTyp a; // Daten vom Typ 'EinTyp' private List<EinTyp> d; // Referenz auf das nächste ELement /** * Der Konstruktor und alle Objekt-Methoden sind nicht * öffentlich. */ protected List(EinTyp ap, List dp) { a = ap; d = dp; } protected EinTyp car() { return a; } protected List<EinTyp> cdr() { return d; } ...
List<Integer> x = new List<Integer>(new Integer(2),null); System.out.println("x = " + x); List<Double> y = new List<Double>(new Double(3.0),null); System.out.println("y = " + y); // Fehler: kein Integer in Double Liste y = List<Double>.cons( new Integer(4), y); System.out.println("y = " + y); List<Integer> z = null; for (int i = 0; i < 10; i++) { z = List<Integer>.cons( new Integer(i), z); } System.out.println("z = " + z);
© Universität Mannheim, Rechenzentrum, 2002-2004.
Heinz KredelLast modified: Sun Oct 24 13:38:58 CEST 2004