• Keine Ergebnisse gefunden

5.3.3 Vererbung Begriffsklärung: (Vererbung)

N/A
N/A
Protected

Academic year: 2022

Aktie "5.3.3 Vererbung Begriffsklärung: (Vererbung)"

Copied!
7
0
0

Wird geladen.... (Jetzt Volltext ansehen)

Volltext

(1)

05.02.2007 © A. Poetzsch-Heffter, Universität Kaiserslautern 647

5.3.3 Vererbung

Begriffsklärung: (Vererbung)

Vererbung (engl. inheritance) im engeren Sinne bedeutet, dass eine Klasse Programmteile von einer anderen übernimmt.

Die erbende Klasse heißt Subklasse, die vererbende Klasse heißt Superklasse.

In Java sind die ererbten Programmteile Attribute, Methoden und geschachtelte Klassen, nicht vererbt werden Klassenattribute, Klassenmethoden und Konstruktoren.

In Java ist die Subklasse immer ein Subtyp des Typs der Superklasse.

Vererbung unterstützt Spezialisierung durch:

- Hinzufügen von Attributen (Zustandserweiterung) - Hinzufügen von Methoden (Erweiterung der

Funktionalität)

- Anpassen, Erweitern bzw. Reimplementieren von Supertyp-Methoden (Anpassen der Funktionalität)

05.02.2007 © A. Poetzsch-Heffter, Universität Kaiserslautern 648

Beispiel: (Vererbung)

class Person { String name;

int gebdatum; /* Form JJJJMMTT */

void drucken() {

System.out.println("Name: "+ this.name);

System.out.println("Gebdatum: "+gebdatum);

}

boolean hat_geburtstag ( int datum ) { return (gebdatum%10000)==(datum%10000);

}

Person( String n, int gd ) { name = n;

geburtsdatum = gd;

} }

class Student extends Person { int matrikelnr;

int semester;

void drucken() { super.drucken();

System.out.println("Matnr: "+ matrikelnr);

System.out.println("Semzahl: "+ semester);

}

Student(String n,int gd,int mnr,int sem) { super( n, gd );

matrikelnr = mnr;

semester = sem;

} }

Vererben von Attributen:

class C {

public int a = 0;

int b = 1;

private int c = 2;

static int d = 3;

int getC() { return c; } }

class D extends C {

int getB() { return b; } }

public class Attributvererbung {

public static void main( String[] args ) { D dv = new D();

System.out.println("a,D-Obj:"+ dv.a);

System.out.println("b,D-Obj:"+ dv.getB());

System.out.println("c,D-Obj:"+ dv.getC());

System.out.println("C.d: "+ C.d);

D.d = 13;

System.out.println("D.d: "+ D.d);

System.out.println("C.d: "+ C.d);

} }

• Objekte der Subklassen haben für alle nicht-statischen Attribute der Superklasse eine objektlokale Variable.

• Statische Attribute werden nicht in dem Sinn vererbt, dass die Subklasse eine eigene Klassenvariable bekommt.

• Vererbung ist transitiv.

Feststellungen:

Hinzufügen von Attributen:

Um den Zustandsraum in Subklassenobjekten zu erweiteren, können Attribute hinzugefügt werden:

class C {

public int a = 0;

int b = 1;

private int c = 2;

int getC() { return c; } }

class D extends C { public int e = 10;

int b = 11;

public int c = 12;

}

(2)

05.02.2007 © A. Poetzsch-Heffter, Universität Kaiserslautern 651

public class Zustandserweiterung {

public static void main( String[] args ) { D dv = new D();

... dv.e // deklariertes e ... dv.b // deklariertes b ... dv.c // deklariertes c ... dv.a // ererbtes a ... ((C)dv).b // ererbtes b ... dv.getC() // ererbtes c }

}

Folgendes Fragment demonstriert den Zugriff auf die Attribute:

Feststellungen:

• Attribute können ererbte Attribute gleichen

Namens verschatten. Dies sollte vermieden werden, kann aber nicht ausgeschlossen werden.

• Attribute werden statisch gebunden. D.h. : Maßgebend ist der (statische) Typ des selektierten Ausdrucks und nicht der Typ des Objekts, dass sich bei Auswertung des Ausdrucks ergibt.

05.02.2007 © A. Poetzsch-Heffter, Universität Kaiserslautern 652

Vererben von Methoden:

class C {

public int ma(){ return 0; } int mb(){ return 1; } private int mc(){ return 2; } static int md(){ return 3; } int getC() { return mc(); } }

class D extends C { }

public class Methodenvererbung {

public static void main( String[] args ) { D dv = new D();

System.out.println("ma: " + dv.ma() );

System.out.println("mb: " + dv.mb() );

System.out.println("mc: " + dv.getC() );

System.out.println("D.md: "+ D.md() );

} }

Alle Methoden der Superklasse arbeiten auch auf den Objekten der Subklasse. „Vererbt“ werden aber nur die Methoden, die in der Subklasse zugreifbar sind.

Feststellung:

Hinzufügen von Methoden:

Um die Funktionalität von Subklassenobjekten zu erweitern, können Methoden hinzugefügt werden:

class C {

public int ma(){ return 0; } int mb(){ return 1; } private int mc(){ return 2; } int getC() { return this.mc(); } }

class D extends C {

public int me(){ return 10; } public int mc(){ return 12; } }

public class MethodenHinzufuegen {

public static void main( String[] args ) { D dv = new D();

System.out.println("me: " + dv.me() );

System.out.println("mc: " + dv.mc() );

System.out.println("C:mc:"+ dv.getC() );

} }

Auch bei den Methoden kann es zur Verschattung kommen.

Feststellung:

Überschreiben (engl. overriding) einer ererbten Methode m der Superklasse bedeutet, dass man in der Subklasse eine neue Deklaration für m angibt.

Die überschreibende Methode muss in Java die gleiche Signatur wie die überschriebene haben und mindestens so zugreifbar sein. Die überschriebene Methode muss zugreifbar sein und kann durch

„super“-Aufrufe benutzt werden:

Der aktuelle implizite Parameter eines Super-Aufrufs ist der aktuelle implizite Parameter der aufrufenden Methode.

Begriffsklärung: (Überschreiben) Anpassen von Methoden:

In vielen Fällen ist es nötig, die Implementierung einer Methode der Superklasse in der Subklasse anzupassen, insbesondere um den erweiterten Zustand mit zu berücksichtigen.

In den meisten objektorientierten Programmier- sprachen geschieht die Anpassung durch einen Mechanismus, den man Überschreiben nennt:

super.<methodenName>( <AktParam1>,...)

(3)

05.02.2007 © A. Poetzsch-Heffter, Universität Kaiserslautern 655

Beispiel: (Überschreiben)

class C {

private int a = 0;

public void drucke(){

System.out.println("a: " + a);

this.spruch();

}

public void spruch() {

System.out.println("Er erblich.");

} }

class D extends C { private int b = 1;

public void drucke(){

super.drucke();

System.out.println("b: " + b);

this.spruch();

}

public void spruch() {

System.out.println("erblich vorbelastet");

} }

public class UeberschreibenTest {

public static void main( String[] args ) { D dv = new D();

dv.drucke();

} }

05.02.2007 © A. Poetzsch-Heffter, Universität Kaiserslautern 656

Konstruktoren und Vererbung:

Konstruktoren werden in Java nicht vererbt; d.h. wenn eine Subklasse keinen eigenen Konstruktor deklariert, steht nur der default-Konstruktor zur Verfügung.

Jeder Konstruktor kann in seiner ersten Anweisung einen Konstruktor der Superklasse aufrufen. Impliziter Parameter ist das neu erzeugte Objekt (Syntax siehe Beispiel).

Fehlt ein expliziter Aufruf eines Superklassen- Konstruktors, wird implizit der Konstruktor mit leerer Parameterliste aufgerufen.

Gibt es keinen solchen Konstruktor oder ist er nicht zugreifbar, meldet der Übersetzer einen Fehler.

Beachte:

Überschreiben findet nur statt, wenn die Methode in der Subklasse zugreifbar ist.

class Superklasse { String a;

int b;

Superklasse(){ a = "\"Java ist "; } Superklasse( int i ){

a = "Auch "+ new Integer(i).toString();

} }

class Subklasse extends Superklasse { Subklasse( String s ){

a = a + s;

}

Subklasse( int i, int j ){

super(i*j);

a = a + " Wiederholungen machen Legenden";

} }

class VererbungsTest {

public static void main( String[] args ){

Subklasse sk = new Subklasse("einfach.\"");

System.out.println( sk.a );

sk = new Subklasse(100,10);

System.out.print( sk.a );

System.out.println(" nicht wahr.");

} }

Beispiel: (zum Umgang mit Konstruktoren) Diskussion von Vererbung

Vererbung ist ein mächtiges Sprachkonzept.

Das Konzept ist im Kern einfach:

Statt Programmcode explizit von der Super- in die Subklassen zu kopieren, steht der vererbte Code automatisch in der Subklasse bereit.

Vorteile gegenüber explizitem Kopieren & Einfügen:

- zuverlässiger

- Reduktion der Programmcodegröße - besser zu warten/pflegen

- Spezialisierung unzugänglicher Programmteile wird erleichtert

Die sprachliche Umsetzung führt bei den meisten Sprachen zu komplexen Wechselwirkungen zwischen den Konstrukten:

- zur Vererbung

- zum Information Hiding (private, protected, ...) - zur dynamischen Methodenauswahl.

(4)

05.02.2007 © A. Poetzsch-Heffter, Universität Kaiserslautern 659

Beispiel: (Konstruktoren/dyn. Bindung)

class Oberklasse { String a;

Oberklasse(){

a = "aha";

m();

}

void m(){

System.out.print("Laenge a:"+a.length());

} }

class Unterklasse extends Oberklasse { String b;

Unterklasse(){

b = "boff";

m();

}

void m(){

System.out.print("Laenge b:"+b.length());

} }

class KonstruktorProblemTest {

public static void main( String[] args ){

new Unterklasse();

} }

05.02.2007 © A. Poetzsch-Heffter, Universität Kaiserslautern 660

In Verbindung mit Subtyping ist Vererbung:

- ein sehr mächtiger Strukturierungsmechanismus, - der die Entwicklung offener Programme unterstützt - und Wartbarkeit und Lesbarkeit verbessert

(bei geeignetem Einsatz).

Subclassing = Subtyping + Vererbung

Wir betrachten den Zusammenhang zwischen Subtypbildung und Vererbung. Für Java ist das insbesondere der Zusammenhang von

- Schnittstellentypen - abstrakten Klassen - (vollständigen) Klassen

Begriffsklärung: (abstrakte Meth. & Klassen)

Eine Methode heißt abstrakt, wenn für sie kein Rumpf angegeben ist. Eine Klasse heißt abstrakt, wenn sie abstrakte Methoden besitzt oder als abstrakt deklariert ist (Modifikator ).

Es ist unzulässig, Instanzen abstrakter Klassen zu erzeugen.

• Schnittstellentyp:

- keine Attribute, keine Methodenimplementierung - Typ umfasst alle Objekte der Subklassen

• Typ deklariert durch abstrakte Klasse:

- Attribute, Methodenimplementierung (Vererbung) - Typ umfasst alle Objekte der Subklassen

• Typ deklariert durch vollständige Klasse K:

- Attribute, Methodenimplementierung (vollständig) - Objekterzeugung

- Typ umfasst die Objekte von K und alle Objekte in Subklassen

Abstrakte Klassen stehen zwischen Schnittstellen und vollständigen Klassen:

Den Zusammenhang zwischen diesen Sprach- konzepten studieren wir anhand folgenden Beispiels.

A

void mm() void mp()

Beispiel: (Realisieren von Typhierarchien)

B

void mm() void mq()

C void mm() void mp() void mq() void mr()

D void mm() void mp() void mq() void ms() Drei Realisierungsvarianten:

1. Nur Subtyping, keine Vererbung:

- A und B als Schnittstellen - C und D als Klassen

2. Einfache Vererbung von einer Klasse:

- A als abstrakte Klasse - B als Schnittstelle

- C und D als Klassen, erben von A.

3. Mehrfachvererbung:

- A, B, C und D als Klassen; C, D erben von A u. B.

(5)

05.02.2007 © A. Poetzsch-Heffter, Universität Kaiserslautern 663

1. Variante:

interface A { void mm();

void mp();

}

interface B { void mm();

void mq();

}

class C implements A, B { int a, b, c;

public void mm(){

c = 2000; ...

{ // dieser Block benutzt nur Attribut a // und ist identisch mit entsprechendem // Block in Klasse D

... a ...

}

c = a + c;

}

public void mp(){

// benutzt die Attribute a und c }

public void mq(){

b = 73532;

}

public void mr(){ ... } }

05.02.2007 © A. Poetzsch-Heffter, Universität Kaiserslautern 664

class D implements A, B { int a, b;

String d;

public void mm(){

// dieser Block benutzt nur Attribut a // und ist identisch mit Block in Klasse C ... a ...

}

public void mp(){

// benutzt die Attribute a und d }

public void mq(){

b = 73532;

}

public void ms(){ ... } }

C und D haben das Attribut a und den Block in der Methode mm gemeinsam. Diese Programmteile lassen sich in A zusammenfassen.

2. Variante:

abstract class A { int a;

public void mm() {

// der Block von mm in C aus der ersten // Variante bzw. der Rumpf von mm in D ... a ...

}

public abstract void mp();

}

class C extends A implements B { int b, c;

public void mm(){

c = 2000; ...

super.mm();

c = a + c;

}

public void mp(){

// benutzt die Attribute a und c }

public void mq(){ b = 73532; } public void mr(){ ... }

}

class D extends A implements B { int b;

String d;

public void mp(){

// benutzt die Attribute a und d }

public void mq(){ b = 73532; } public void ms(){ ... }

}

In der zweiten Variante ist der Typ B nach wie vor als Schnittstelle realisiert. Die Klasse C und D erben von der abstrakten Klasse A:

Diese Variante ist knapper als die erste.

3. Variante:

Begriffsklärung: (Mehrfachvererbung)

Übernimmt eine Klasse Programmteile von mehreren anderen Klassen spricht man von Mehrfachvererbung (engl. multiple inheritance).

C++ unterstützt Mehrfachvererbung, Java nicht.

Wir illustrieren Mehrfachvererbung hier mit einer fiktiven Spracherweiterung von Java, die Mehrfachvererbung unterstützt.

Klasse A bleibt wie in Variante 2. Typ B wird durch eine Klasse realisiert:

class B { int b;

public void mm(){

... // gemaess den Anforderungen von B }

public void mq(){ b = 73532; } }

Die Klassen C und D können nun von B erben.

Allerdings müssen sie den Konflikt bzgl. mm auflösen.

(6)

05.02.2007 © A. Poetzsch-Heffter, Universität Kaiserslautern 667

class C extends A, B {// KEIN Java!!!

int c;

public void mm(){

c = 2000; ...

super.A:mm();

c = a + c;

}

public void mp(){

// benutzt die Attribute a und c }

public void mr(){ ... } }

class D extends A,B { // KEIN Java!!!

String d;

public void mm(){

super.A:mm();

}

public void mp(){

// benutzt die Attribute a und d }

public void ms(){ ... } }

Da in Java Mehrfachvererbung nicht möglich ist, muss man es durch Einfachvererbung und

mehrfache Subtypbildung ersetzen, z.B. wie in der zweiten Variante demonstriert.

05.02.2007 © A. Poetzsch-Heffter, Universität Kaiserslautern 668

Vererbung und Information Hiding

Durch die Vererbung gibt es nun zwei Arten, eine Klasse K zu nutzen:

- Anwendungsnutzung: Erzeugen und nutzen der Objekt von K.

- Vererbungsnutzung: Spezialisieren und erben von K.

Damit die erbende Klasse die geerbten Programm- teile geeignet nutzen kann, benötigt sie meist einen intimeren Zugriff als ein Anwendungsnutzer.

Deshalb gibt es einen Zugriffsbereich für Vererbung, der alle Subklassen einer Klasse umfasst.

Programmelemente, die als geschützt deklariert sind, d.h. mit dem Modifikator protected, sind in allen Subklassen zugreifbar.

Will man also Programmelemente, insbesondere Attribute, für Subklassen bereitstellen, müssen sie mindestens geschützten Zugriff gewähren.

Geschützter Zugriff ermöglicht allerdings erhebliches Verändern einer Klasse in Subklassen und birgt dementsprechend auch Gefahren, wie folgendes Beispiel zeigt.

Geschützter Zugriff:

Beispiel: (geschütztes Zugriffsrecht)

package soweitAllesOk;

public class A_nicht_Null { protected int a = 1;

public int getA() { return a; } protected void setA( int i ) {

if( i>0 ) a = i;

} }

public class Anwendung { ...

public static void m( A_nicht_Null ap ){

float f = 7 / ap.getA();

} }

Die Anwendung kann hier davon ausgehen, dass die Instanzvariable a nie den Wert 0 annimmt.

package einHackMitZweck;

import soweitAllesOk.*;

public class A_doch_Null extends A_nicht_Null {

public int getA() { return -a; } public void setA( int i ) { a = i; } }

public class Main {

public static void main( String[] args ) { A_doch_Null adn = new A_doch_Null();

adn.setA( 0 );

A_nicht_Null ann = adn;

... // hier könnte die Herkunft von // ann verschleiert sein

Anwendung.m(ann);

} }

Durch Vererbung können Subtyp-Objekte erzeugt werden, die sich ganz anders als die Objekte der Superklasse verhalten:

Um Szenarien wie im obigen Beispiel zu vermeiden, sollten Subklassen-Objekte das Verhalten der Superklassen-Objekte spezialisieren und sich ansonsten konform verhalten.

(7)

05.02.2007 © A. Poetzsch-Heffter, Universität Kaiserslautern 671

Zusammenfassende Bemerkungen zu 5.3

• Subtypen erlauben es, spezialisierte Objekte anstelle von Supertyp-Objekten zu verwenden.

Dadurch können Programme auf der Ebene allgemeinerer Objekte formuliert und wiederverwendet werden.

• Vererbung erlaubt die Weitergabe und damit Wiederverwendung von Programmteilen der Superklasse an die Subklasse.

• Subtypen in Kombination mit Vererbung erlauben eine direkte Realisierung von Klassifikationen im Rahmen der Programmierung.

• Die Vorteile wirken sich vor allem bei der Entwicklung von Programmbibliotheken und Programmgerüsten/Frameworks aus.

Referenzen

ÄHNLICHE DOKUMENTE

Auf das Attribut besitzer soll lesend zugegriffen werden können. Schreibe dazu in der Klasse BALL eine sondierende Methode für besitzer und ändere den Quelltext von TEST

Auf das Attribut besitzer soll lesend zugegriffen werden können. Schreibe dazu in der Klasse BALL eine sondierende Methode für besitzer und ändere den Quelltext von TEST

Jede Klasse in Java ist von der Klasse Objekt abgeleitet, ohne dass wir dies explizit definieren müssen.. Wie wir in der Abbildung sehen, können wir in Java eine Klasse Person

§8.1.3: The optional extends clause in a class declaration specifies the direct superclass of the current class.. A class is said to be a direct subclass of the class

Deshalb gibt es einen Zugriffsbereich für Vererbung, der alle Subklassen einer Klasse umfasst. Programmelemente, die als geschützt deklariert

erbt: code, price, available, print ergänzt: author, title, Konstruktor überschreibt: print. Article code price available() print() Article(

n Mehrfachvererbung: Klasse kann von mehr als einer Superklasse abgeleitet sein (nicht im Sinne von Interfaces in Java).. n Late Binding: in Java immer; in C++ steuerbar

► Vererbung: Eine Klasse kann Merkmale von einer Oberklasse übernehmen..