Wirtschaftsinformatik (Bachelor-Studiengang): Programmierung (2. Semester)

Sie sind hier: StartseiteWirtschaftsinformatikProgrammierung Java (Kurs 2): Exkurs Objektorientierte Programmierung

HB / CM, Kurs vom 01.10.2002 - 31.03.2003

Programmierung Java (Kurs 2): Exkurs Objektorientierte Programmierung: Vererbung (Warum ist Vererbung so wichtig?, Wann soll eine Klasse B von A erben?), Wie sind abgeleitete Klassen in Java zu deklarieren? (Wie werden Objekte abgeleiteter Klassen erzeugt?, Wie müssen die Konstruktoren abgeleiteter Klassen gestaltet werden?, Wie hat der Aufruf auszusehen?), Das Überschreiben von Attributen und Methoden (Überschreiben von Attributen, Überschreiben von Methoden), Vererbung und Zuweisungskompatibilität (Polymorphismus, Was muss man noch über Vererbung wissen?), Methoden von Object (public int hashCode(), public final Class get Class(), public String toString(), protected Object clone() throws CloneNotSupportedException, Generische Methoden).

  1. Vererbung
  2. Wie sind abgeleitete Klassen in Java zu deklarieren?
  3. Das Überschreiben von Attributen und Methoden
  4. Vererbung und Zuweisungskompatibilität
  5. Methoden von Object

Exkurs Objektorientierte Programmierung:

Im Zentrum steht das Entwickeln von Datentypen. (genannt Klassen)

Dabei ist wichtig: Objekte der Klassen müssen stets in einem "sinnvollen" Zustand sein.

Zum Menü Wirtschaftsinformatik | Zum Seitenanfang

Vererbung

Vererbung ist eine der charakteristischen Eigenschaften objektorientierter Programmierung neben Datenkapselung und Polymorphismus.

Sie besteht darin, dass es bei der Deklaration von Klassen möglich ist, Eigenschaften bereits vorhandener Klassen zu übernehmen.

Wenn eine Klasse B Eigenschaften der Klasse A übernimmt, spricht man von Vererbung, d.h. B erbt die Eigenschaften von A (d.h. alle Attribute und Methoden).

Es gibt verschiedene Terminologien, um die Vererbungsbeziehungen zwischen zwei Klassen A und B auszudrücken:

Vererbung dient der Deklaration von Klassen unter Wiederverwendung bereits existierender Klassen.

Wenn Klasse B Eigenschaften der Klasse A übernimmt, sagt man, dass B von A erbt.

Wenn B von A erbt und C von B erbt, dann ist:

In Java kann jede Klasse nur von einer Basisklasse direkt abgeleitet werden ("erben"). Im Gegensatz zu C++, Eiffel (Mehrfacherbung).

Warum ist Vererbung so wichtig?

Durch die Übernahme bereits existierender Klassen wird die Wiederverwendung von Software unterstützt!

Damit hängt eng der Aspekt der Zuverlässigkeit von Software zusammen.

Voraussetzung für die häufige Wiederverwendung von Klassen ist deren möglichst große Allgemeingültigkeit.

Für das korrekte Anwenden von Vererbung ist das Verstehen der mit der intendierten Semantik:

Wenn ein Klasse B Eigenschaften der Klasse A erbt, bedeutet dies:

  1. Jedes Objekt von B hat alle Attribute und Methoden, die ein Objekt der Klasse A hat.
  2. Darüberhinaus hat jedes Objekt der Klasse B weitere Eigenschaften (Attribute, Methoden), die Objekte der Klasse A nicht haben, d.h. Eigenschaften, in denen es sich von Objekten der Klasse A unterscheidet.

Weil jedes Objekt der Klasse B alle Eigenschaften von Objekten der Klasse A hat, kann es an allen Stellen eingesetzt werden, an denen Objekte der Klasse A zulässig sind. Umgekehrt gilt dies nicht. Man sagt daher, dass Objekte der Klasse B spezieller sind als Objekte der Klasse A und nennt B auch eine Spezialisierung von A.

Dies wird graphisch so ausgedrückt:

  1. Basisklasse
    erbt von,
    ist abgeleitet von
  2. abgeleitete Klasse

Bei Beteiligung mehrerer Klassen kommt es zu sogenannten Klassenhierarchien.

Im Zentrum der objektorientierten Programmierung steht der Entwurf von Klassen und Klassenhierarchien.

Wann soll eine Klasse B von A erben?

In der Literatur wird die Vererbungsbeziehung oft als eine "Ist-ein-Beziehung" bezeichnet.
D.h.: Jedes Objekt der Klasse B ist ein Objekt der Klasse A.
Diese oberflächliche Betrachtungsweise kann aber schnell zu Problemen führen.

Beispiel: jeder Kreis ist eine Ellipse

andererseits:

Hinweis: Keine gute Modellierung!
» Das Vorhandensein einer "Ist-ein-Beziehung" reicht im Allgemeinen nicht aus, um Vererbung zu begründen.

Für das Vorsehen von Vererbung müssen darüber hinaus auch folgende Fragen positiv beantwortet werden:

  1. Soll jedes Objekt der Klasse B alle Eigenschaften von Objekten der Klasse A haben?
  2. Soll jedes Objekt der Klasse B auch in allen Situationen eingesetzt werden können, in denen Objekte der Klasse A zulässig sind?

Dass die "Ist-ein-Beziehung" nicht mit der Vererbungsbeziehung gleichzusetzen ist, sieht man auch an folgendem:

"Vorstellung": Objekt der Klasse B

Objekt der Klasse B

Bildbeschreibung "Objekt der Klasse B": Attribute und Methoden der Klasse B bilden eine Menge. Das Subobjekt der Klasse A ist Teil dieser Menge.

Daher ist klar, dass jedes Objekt der Klasse B alle Eigenschaften hat wie Objekte der Klasse A.

Klar ist auch: Objekte der Klasse B sind nicht Objekte der Klasse A (braucht z.B. mehr Speicherplatz).

Zum Menü Wirtschaftsinformatik | Zum Seitenanfang

Wie sind abgeleitete Klassen in Java zu deklarieren?

public class A
{
...
}
public class B extends A
{
...
}

Es muss möglich sein, auf die Klasse A zuzugreifen. In folgenden Fällen ist das z.B. nicht möglich:

  1. ist nicht public und nicht im Package von B
  2. die Klasse A wurde als final deklariert.

public final class A
{
...
}

Wie werden Objekte abgeleiteter Klassen erzeugt?

Durch einen Konstruktor (wie immer).
Da Konstruktoren nicht zu den Methoden von Klassen gehören, werden sie auch nicht vererbt.

Hinweis: Abgeleitete Klassen haben in der Regel selbstgeschriebene Konstruktoren!

Ausnahme: A hat nur den default-Konstruktor ==> B kann auch nur den default-Konstruktor haben.

Diese Situation ist bei selbstgeschriebenen Basisklassen untypisch.

Wie müssen die Konstruktoren abgeleiteter Klassen gestaltet werden?

Das Erzeugen von Objekten abgeleiteter Klassen erfolgt immer in 3 Schritten:

  1. es wird der Konstruktor der Basisklasse ausgeführt
  2. es werden die Attribute der abgeleiteten Klasse initialisiert
    • entweder mit Hilfe von Initialisierern
    • oder mit default-Werten:
      boolean (false); char ('\n0000'); byte, short (0); int, long (0); float (+ 0.0f); double (+ 0.0); Referenzdatentypen (null)
  3. die weiteren Anweisungen des Konstruktors der abgeleiteten Klasse werden ausgeführt

Jeder Konstruktor einer abgeleiteten Klasse aktiviert zuerst einen Konstruktor der Basisklasse.

Dafür gibt es zwei Möglichkeiten:

  1. die Basisklasse hat nur einen default-Konstruktor
  2. die Basisklasse hat mindestens einen selbstgeschriebenen Konstruktor

im Fall A: alles automatisch
im Fall B: muss der Aufruf eines Konstruktors der Basisklasse die erste Anweisung des Konstruktors der abgeleiteten Klasse sein

Wie hat der Aufruf auszusehen?

Quelltext überspringen

public class A
{
...
public A (String info, int nr)
{
...
}
...
}
public class B
{
...
public B (String text, int n, double x)
{
super(text, n); // aktiviert den Konstruktor der Basisklasse
...
}
public B (double x)
{
this("Nummer", 1, x); // aktiviert den Konstruktor darüber
...
}
}

wegen der Reihenfolge gilt:

Der Konstruktor der abgeleiteten Klasse kann bereits auf Attribute und Methoden der Basisklasse des zuvor erzeugten Teilobjektes des Basisklasse zugreifen.

Der Konstruktor der abgeleiteten Klasse kann bereits die Initialwerte seines Objektes benutzen.

Hinweis: Dadurch kann sich der Zustand eines Objektes während des Konstruktionsprozesses mehrfach ändern!

Zum Menü Wirtschaftsinformatik | Zum Seitenanfang

Das Überschreiben von Attributen und Methoden

Was passiert, wenn ein Attribut oder eine Methode einer abgeleiteten Klasse versehentlich oder bewusst denselben Bezeichner hat, wie ein Attribut oder eine Methode der Basisklasse?

Überschreiben von Attributen

public class A
{
public int a = 1; // public! (ungewöhnlich)
...
}
public class B extends A
{
public int a = -1;
...
}
B x = new(B);
System.out.print(x.a); // ? welches a ?

X Objekt der Klasse B

X Objekt der Klasse B

Bildbeschreibung "X Objekt der Klasse B": Das Methode der Basisklasse überschreibt die Methode der abgeleiteten Klasse.

Hinweis: In dieser Situation kann direkt nur auf eines der beiden Attribute zugegriffen werden. Auf das in der abgeleiteten Klasse neu deklarierte.

Kann ein Objekt der abgeleiteten Klasse auf ein überschriebenes Attribut der Basisklasse zugreifen?

Ja, wenn es public ist, mit Hilfe von super.

int b = super.a; // 1
int c = a; // -1

Kann von außen auf ein überschriebenes Attribut der Basisklasse zugegriffen werden?

Ja, wenn es public ist, mit Hilfe geeigneter Methoden der abgeleiteten Klasse, aber nicht direkt!

public int gibWertVonSuperA()
{
    return super.a;
}

Es gibt noch eine weitere Möglichkeit des Zugriffs (Polymorphie).

Wann soll man das Überschreiben von Attributen einsetzen?

Gar nicht! Wenn alle Attribute private sind, ist der Zugriff von außen immer nur über Methoden möglich. Dies führt im schlimmsten Fall zum Überschreiben von Methoden, nicht von Attributen.

Warum muss das Überschreiben von Attributen dennoch geregelt werden?

Damit Basisklassen, von denen bereits eine Vielzahl von anderen Klassen abgeleitet wurden, auch noch nachträglich um Attribute erweitert werden können, die public oder protected sind!

Hinweis: Attribute sind so, dass es zu keinen Störungen kommen kann!

Überschreiben von Methoden

Wird sehr häufig eingesetzt!

Warum:

Objekte abgeleiteter Klassen sind spezieller als Objekte ihrer Basisklassen.
Daher sollten manche ihrer Methoden ebenfalls spezieller sein, als die gleichnamigen Methoden ihrer Basisklassen! (spezieller: höherer Funktionsumfang)

Quelltext überspringen

public class Angestellter
{
private String name;
private double gehalt;
...
public double gibGehalt()
{
return gehalt;
}
...
}
public class Manager extends Angestellter
{
private double praemie;
...
public void setzePraemie(double p)
{
praemie = p;
}
...
public double gibGehalt()
{
return praemie + supter.gibGehalt();
}

Die überschriebene Methode der Basisklasse ist vorhanden, man kann aber nicht mehr direkt auf sie zugreifen!

Manager x = new Manager();
x.gibGehalt(); // Methode der Klasse Manager wird aktiviert

Sehr häufig werden überschreibende Methoden nach folgendem Schema gestaltet:

public class A
{
public void machWas()
{
...
}
}
public class B extends A
{
...
public void machWas()
{
super.machWas();
... // mach den Rest
}
}

Zugriff von außen auf überschriebene Methoden von Basisklassen ist mit Hilfe von Methoden der abgeleiteten Klasse möglich.

public double gibGrundGehalt()
{
return super.gibGehalt();
}

Was ist beim Überschreiben von Methoden zu beachten?

Ein Überschreiben liegt nur dann vor, wenn die Signatur und der Datentyp des Rückgabewertes der Methode in der abgeleiteten Klasse und in der Basisklasse übereinstimmen.

Wenn eine Methode der abgeleiteten Klasse im Namen mit einer Methode der Basisklasse übereinstimmt, aber eine andere Parameterliste hat (mehr, weniger, andere), handelt es sich um das Überladen von Methoden, nicht das Überschreiben. Dies ist zulässig und kommt häufig vor.

Wenn eine Methode der abgeleiteten Klasse dieselbe Signatur hat wie eine Methode der Basisklasse, aber einen anderen Datentyp für den Rückgabewert, ist das ein Fehler!

Methoden einer Klasse, die private sind, können nicht überschrieben werden.

Der Zugriffsschutz der überschreibenden Methoden kann ein anderer sein, als der der überschriebenen Methoden, aber nicht beliebig.

Der Zugriff kann nie verschärft werden!

Die Zugriffsmodifizierer native, synchronized und strictfp können beliebig ausgetauscht werden.

Die Methode der abgeleiteten Klasse kann final sein (die der Basisklasse aber nicht). Parameter der Methode der abgeleiteten Klasse können final sein, obwohl dieselben Parameter der Methode der Basisklasse nicht final waren.

Bei Exceptions gibt es einige Einschränkungen. (Selbststudium)

Statische Attribute und Methoden können nicht überschrieben werden.

Zum Menü Wirtschaftsinformatik | Zum Seitenanfang

Vererbung und Zuweisungskompatibilität

Da Java streng typisiert ist, prüft der Compiler bei jeder Wertzuweisung, ob der Datentyp des zugewiesenen Wertes kompatibel ist zum Datentyp des Empfängers.

Empfänger können unter anderem sein:

Liegt keine Zuweisungskompatibilität vor, kommt es zu einem Fehler.

Für die primitiven Datentypen ist Zuweisungskompatibilität exakt geregelt, für die Referenzdatentypen auch.

Für Referenzdatentypen gilt:

Null ist zuweisungskompatibel zu allen Referenzdatentypen (Array, Klassen, Interfaces).

Wenn der Referenzdatentyp A eine Klasse ist, ist der Referenzdatentyp B zuweisungskompatibel in folgenden Situationen:

  1. B ist identisch mit A
  2. B ist abgeleitet von A

Wenn der Referenzdatentyp A[ ] ein Array-Datentyp ist, ist der Array-Datentyp B[ ] zuweisungskompatibel zu A[ ] in folgender Situation:

  1. A und B sind identische primitive Datentypen
  2. A und B sind Referenzdatentypen und B ist zuweisungskompatibel zu A

Darüber hinaus gibt es Regeln für Interfaces.

Die Regeln für Klassen sind plausibel, da jedes Objekt einer abgeleiteten Klasse alle Eigenschaften der Basisklasse hat und an allen benutzt werden können sollte, an dem Objekt der Basisklasse zulässig sind.

Interessant wird die Situation, wenn es gleichzeitig überschriebene Attribute und/oder überschriebene Methoden gibt.

Quelltext überspringen

public class A
{
public int a = 1; // public untypisch
public schreibeTyp()
{
System.out.println("Klasse A");
}
}
public class B
{
public int a = -1; // public untypisch, überschreibt a!
public int b = 100;
public schreibeTyp() // überschreibt!
{
System.out.println("Klasse B");
}
...
A x = new A();
System.out.println("x.a = " + x.a); // 1
x.schreibeTyp();
...
B y = new B();
System.out.println("y.a = " + y.a); // -1
y.schreibeTyp();
...
A z = y; // ok! Regel 2!
System.out.println("z.a = " + z.a);
z.schreibeTyp();

überschriebene Attribute und/oder überschriebene Methoden

Bildbeschreibung "überschriebene Attribute und/oder überschriebene Methoden": Graphische Darstellung von Vererbung und Zuweisungskompatibilität.

Welchen Wert hat z ?
Welche Methode wird bei z.schreibeTyp() aufgerufen?

Für den Zugriff auf überschriebene Attribute gilt:

y.a // - 1
z.a // 1

Der Datentyp des Objektes entscheidet darüber, auf welche Methode zugegriffen wird.

y.schreibeTyp() // Klasse B
z.schreibeTyp() // Klasse B

Polymorphismus

Aus Sicht der Referenzvariablen bedeutet dies:

Hinweis: Was genau eintritt, hängt also von der Situation ab. Das heißt, das Verhalten ist nicht genau vorhersehbar, sondern polymorph (vielgestaltig).

Aus Sicht des Objektes bedeutet dies,

Hinweis: polymorphes Verhalten

Polymorphismus ist die dritte charakteristische Eigenschaft objektorientierter Sprache (neben Datenkapselung und Vererbung).

Für den Aufruf von Methoden bedeutet dies, dass vom Compiler erst zur Laufzeit entschieden werden kann, welche Methode genau ausgeführt wird.

x.Methode() // welche Methode, dynamisch

Diese dynamische Bindung kostet Zeit, was ein weiterer Grund für die nachgesagte Langsamkeit objektorientiert Sprachen ist. Intern wird dynamische Verbindung mit Hilfe sogenannten Methodentabellen realisiert.

Für Arrays bedeuten die Regeln, dass die Komponenten eines Arrays von Klassen nicht alle denselben Datentyp haben müssen!

Quelltext überspringen

...
A[ ] feld = new A[ 3 ];
...
feld[ 0 ] = new A();
feld[ 1 ] = new B();
feld[ 2 ] = new A();
for (int i = 0; i < feld.length; i++)
System.out.println(feld[ i ].a); // 1 1 1
for (int i = 0; i < feld.length; i++)
feld[ i ].schreibeTyp(); // Klasse A Klasse B Klasse B

Hinweis: Upcasting

Downcasting ist nur mit Hilfe des cast-Operators möglich.

A x = new B();
System.out.println(x.a); // 1
System.out.println( (B) x).a); // -1

Mit dem binären instanceof-Operator kann festgestellt werden, ob ein Ausdruck zuweisungskompatibel ist zu einem Datentyp.

x instanceof Referenzdatentyp

Quelltext überspringen

for (int i = 0; i < feld.length; i++)
{
if (feld[ i ] instanceof B)
System.out.println( ( (B)feld[ i ]).a);
else
System.out.println(feld[ i ].a); // 1 -1 1

Was muss man noch über Vererbung wissen?

protected kann sowohl für Attribute als auch für Methoden eingesetzt werden.

Auf protected-Attribute oder -Methoden kann zugegriffen werden in folgenden Situationen:

protected bietet daher nur einen geringen Zugriffsschutz.
protected für Attribute ist selten sinnvoll ("kaum eingesetzt").
Direkter Zugriff ist zwar nicht möglich, man braucht aber nur eine Klasse abzuleiten, und hat von dieser dann direkten Zugriff.

Hinweis: So kann Datenkapselung umgangen werden. Schlecht!

Bei Methoden kann protected zum Beispiel in folgenden Situationen sinnvoll sein:

Hinweis: Durch protected wird der Zugriffsschutz erheblich gelockert. Daher nur sparsam damit umgehen!

Methoden, die als final deklariert wurden, können beim Ableiten nicht überschrieben werden. Dadurch kann zum Beispiel sichergestellt werden, dass sicherheitsrelevante Methoden (Passwort-Methoden) oder spezielle Algorithmen nicht verändert werden können.

Der Aufruf von final-Methoden kann vom Compiler optimiert werden, da dynamische Bindung durch statische Bindung ersetzt werden kann.

Hinweis: positiver Effekt auf Laufzeitverhalten.

Andererseits wird durch final das Überschreiben verhindert und damit die Vererbung beschränkt.

Hinweis: final nicht unüberlegt einsetzen!

Noch drastischer ist die Einschränkung, eine gesamte Klasse als final zu deklarieren. Von derartigen Klassen kann nicht geerbt werden.

Die Java Klassen-Bibliothek enthält eine Reihe von Klassen, die als final deklariert wurden.

Alle Klassen in Java erben entweder direkt oder indirekt von der vordefinierten Klasse Object. Object ist die Wurzel der gesamten Java-Klassen-Hierarchie.

Object

Bildbeschreibung "Object": Alle Klassen in Java erben entweder direkt oder indirekt von der vordefinierten Klasse Object. Object ist die Wurzel der gesamten Java-Klassen-Hierarchie.

In C++ gibt es keine gemeinsame Wurzel für alle Klassen.

Es ist nicht zulässig, bei der Deklaration von Klassen Object anzugeben! Dies geschieht gegebenenfalls automatisch.

Die Klasse Object hat keine Attribute, aber elf Methoden, die alle Klassen in Java erben. Fünf dieser Methoden sind nur im Zusammenhang mit "Parallelverarbeitung" mit Hilfe von threads relevant.

protected void finalize() throws Throwable:

Diese Methode wird vom Garbage-Collector unmittelbar vor dem Zerstören von Objekten aktiviert. Eine typische Anwendung von finalize sieht folgendermaßen aus:

public boolean equals(Object y):

Diese Methode liefert true, wenn das aktuelle Objekt und y "gleich" sind, ansonsten false.

Wann sind zwei Objekte gleich?

Wenn equals nicht überschrieben wurde, bedeutet "gleich" lediglich, dass es sich um Referenzen auf dasselbe Objekt handelt.

A x, y;
x = new A();
y = x;
boolean gleich = x.equals(y); // true

Für viele Anwendungen ist dieser Begriff von "Gleichheit" nicht angemessen.

Quelltext überspringen

public class Punkt
{
double x;
double y;
...
}
...
Punkt a = new Punkt(1.1, 3.57);
Punkt b = new Punkt(1.1, 3.57);
boolean gleich = a.equals(b); // false ==> nicht sinnvoll

Hinweis: In derartigen Situationen equals überschreiben! Achtung: ein sinnvolles Überschreiben von equals ist nicht trivial!

In der Java-Klassen-Bibliothek gibt es mehr als 150 Überschreibungen von equals, die nach Auffassung dieser Autoren nicht korrekt sind.

Zum Menü Wirtschaftsinformatik | Zum Seitenanfang

Methoden von Object

public int hashCode()

Liefert zu jedem Objekt eine ganze Zahl, so dass folgende Bedingungen erfüllt sind:

Der Hashcode von Objekten wird für die Datenorganisation mit sogennanten Hashtabellen gebraucht.

Über die Bedingung 2 sind equals und hashCode miteinander verknüpft.

Hinweis: Wird equals überschrieben, muss hashCode entsprechend angepasst werden!

public final Class get Class()

Liefert zum aktuellen Objekt ein Objekt der Klasse Class, das sogenannte Laufzeitobjekt der Klasse, zu der das Objekt gehört.

Class verfügt über 36 Methoden, mit denen - während der Laufzeit - diverse detaillierte Informationen über das Objekt und seine Klasse erhalten werden können.

public String toString()

Liefert eine Darstellung des aktuellen Objektes als Zeichenkette.

Wenn toString nicht überschrieben wird, besteht die Zeichenkette aus einer Information über die Klasse und aus dem Hashcode des Objektes als Hexadezimalzahl.

Quelltext überspringen

[I@7182c1
[I => int-Array
7182c1 => Hashcode (der aus der Adresse des Objektes erzeugt wird)

Hinweis: Daher ist es sinnvoll, toString in allen selbstgeschriebenen Klassen zu überschreiben!

Quelltext überspringen

A x = new A();
System.out.print("Wert von x:" + x);
x.toString()

Wenn toString sinnvoll überschrieben wird, werden Methoden der Art "schreibeObjekt" überflüssig!

Quelltext überspringen

public class Punkt
{
double x;
double y;
public Punkt(doublex, double y)
{
this.x = x;
this.y = y;
}
void schreibePunkt()
{
System.out.print("(" + x + ", " + y + ")");
}
}

Das ganze ist mit toString nicht notwendig:
public to String()
{
return("(" + x + ", " + y + ")");
}
...
Punkt a = new(1.1, 4.33);
a.schreibePunkt(); // (1.1, 4.33)
System.out.print(" " + a); // (1.1, 4.33)

protected Object clone() throws CloneNotSupportedException

Diese Methode dient dazu, eine exakte Kopie des aktuellen Objektes zu erzeugen.

Sie kann nur eingesetzt werden, wenn das Interface Cloneable benutzt wird.

Meistens ist clone zu überschreiben. Das aber ist nicht ganz trivial. Stichwort "deep cloning".

Da Object die Wurzel der gesamten Java-Klassenhierarchie ist, sind alle Referenzvariablen in Java zuweisungskompatibel zu Referenzen auf Object.

...
Object x;
...
A y = new A();
x = y; // Okay!

Dies gilt nicht für Variablen, die von einem primitiven Datentyp sind.

int z = 10;
...
x = z; // Fehler!

Daher gibt es zu jedem primitiven Datentyp eine sogenannte Wrapper-Klasse, die es gestattet, aus dem Wert eines primitiven Datentyps ein entsprechendes Objekt zu machen.

Die Objekte der Wrapper-Klassen sind zuweisungskompatibel zu Object-Referenzen.

Object x = new Integer(-7); // Okay!

Jede der Wrapper-Klassen stellt eine Reihe nützlicher Methoden zur Verfügung.
Klasse Attribute Konstruktoren eigene Methoden
Byte 3 2 17
Short 3 2 17
Integer 3 2 24
Long 3 2 24
Character 55 1 34
Float 6 3 22
Double 6 2 22
Boolean 3 2 8

Object-Arrays können Referenzen auf beliebige Klassen aufnehmen. Durch den Einsatz von Polymorphismus und Downcasting ergeben sich (völlig neue) interessante Möglichkeiten.

Quelltext überspringen

public class Tier
{
private String art;
public Tier (String dieArt)
{
art = dieArt;
}
public void sprich()
{
System.out.print("Ich kann nicht sprechen!")
}
Public String toString()
{
return art + ": ";
}
}
public class Hund extends Tier
{
private String name;
private String rasse;
public Hund(String dieArt, String derName, String dieRasse)
{
super(dieArt);
name = derName;
rasse = dieRasse;
}
public void sprich()
{
System.out.println(" Ich mache wau, wau!");
}
public String toString()
{
return super.toString() + " Ich bin ein " + rasse + " und heisse " + name + ".";
}
}
... }

Generische Methoden

Dies ermöglicht die Gestaltung generischer Methoden.

Quelltext überspringen

int finde(Object[] feld, Object wert)
{
int fundStelle = -1;
boolean gefunden = false;
for(int i=0; !gefunden && i<feld.length; i++)
if (feld[i].equals(wert))
{
fundStelle = i;
gefunden = true;
}
return fundStelle;
}

Diese Methode funktioniert für alle Arrays mit Referenzen auf Klassen, für die die Methode equals sinnvoll definiert ist! (=> "generische Programmierung")