PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Java + Schlüsselwort "extends"


RMC
2005-10-27, 19:46:36
Ich hab mal von einer Möglichkeit gehört, mit der man einfach mit dem Schlüsselwort extends überprüfen kann, ob eine Klasse von einer anderen ableitet.

Also statt der bisherigen Methode:


try
{
Class1 c1 = (Class1) variable;
}
catch(ClassCastException cce)
{
System.out.println("kann nicht gecastet werden.");
}



gibts anscheinend eine vereinfachte Methode, zb so ähnlich wie:


if(variable extends Class1)
{
...
}



Kann das sein? Und wenn ja wie gehts richtig? :rolleyes: Danke!

Abnaxos
2005-10-27, 19:58:11
Nein, das kann so nicht sein. ;) Du vermischt da zwei Dinge.

Für das, was du tun willst, ist richtig:

if ( variable1 instanceof Class1 ) {
// ...
}

Ab Java5 gibt es bei den Generics eine Schreibweise, die etwas Ähnliches macht und mit "extends" funktioniert:

class MyClass<T extends Collection> {
// ...
}

Damit sagst du, dass für das Typargument der Klasse MyClass<T> T instanceof Collection == true gelten muss.

Jetzt muss ich noch erwähnen: Wann immer du "instanceof" brauchst, solltest du dir überlegen, ob du nicht gerade an einem Workaround um einen Design-Fehler werkelst. "instanceof" hat in der OOP eigentlich nichts zu suchen.

SGT.Hawk
2005-10-27, 20:07:48
Richtig, denn anstatt jedesmal bevor man eine Operation ausführt auf Typenkompatibilität überprüfen muss, hat man einen Designerfehler.
Was er hier meint ist, dass man zu gleichen Klassen ein abstrakte Oberklasse haben sollte,denn somit wird ein instanceof überflüssig.

RMC
2005-10-27, 20:46:53
Richtig, denn anstatt jedesmal bevor man eine Operation ausführt auf Typenkompatibilität überprüfen muss, hat man einen Designerfehler.
Was er hier meint ist, dass man zu gleichen Klassen ein abstrakte Oberklasse haben sollte,denn somit wird ein instanceof überflüssig.


äh...ich hab eine abstrakte Oberklasse und 2 Klassen, die von dieser ableiten, aber ich komm um ein "instanceof" bzw. um einen Typcheck trotzdem nicht herum.

Trap
2005-10-27, 20:47:33
In den meisten Fällen ist es sinnvoller instanceof durch das Visitor-Pattern oder eine Programmiersprache mit multi-dispatch zu ersetzen.

Ich empfehle das 2. wenn keine Rahmenbedingungen dagegen sprechen.

Senior Sanchez
2005-10-27, 20:47:52
Jetzt muss ich noch erwähnen: Wann immer du "instanceof" brauchst, solltest du dir überlegen, ob du nicht gerade an einem Workaround um einen Design-Fehler werkelst. "instanceof" hat in der OOP eigentlich nichts zu suchen.


Das ist so nicht ganz richtig.
Es gibt ja zum Beispiel das schöne Design Pattern mit Marker-Interfaces indem über die Implementierung eines (leeren) Interfaces eine Klasse markiert wird, sodass sie gesondert behandelt werden kann. Das ganze nutzen afaik auch zum Teil Java Klassen, bestes Beispiel ist afaik Serializable oder auch Remote, das sind beides Marker-Interfaces.

Abnaxos
2005-10-27, 20:56:21
Naja, ich habe ja auch geschrieben: "Man sollte sich überlegen, ob nicht ein Design-Fehler vorliegt" -- das heisst nicht zwingend, dass es einer sein muss. Natürlich gibt es Ausnahmen, gerade in Frameworks oder Containern für Komponenten können solche Konstruktionen in der Tat sehr praktisch sein.

Ab Java5 würde ich dieses Beispiel allerdings mit Annotations lösen. Im Prinzip ist auch das Marker-Interface nur ein Workaround um fehlende Features in der Sprache. Ein anderes Beispiel ist das bekannte Konstanten-Interface, das seit Java5 eigentlich durch Enumerations und Static Imports ersetzt ist.

Senior Sanchez
2005-10-27, 21:01:28
Naja, ich habe ja auch geschrieben: "Man sollte sich überlegen, ob nicht ein Design-Fehler vorliegt" -- das heisst nicht zwingend, dass es einer sein muss. Natürlich gibt es Ausnahmen, gerade in Frameworks oder Containern für Komponenten können solche Konstruktionen in der Tat sehr praktisch sein.

Ab Java5 würde ich dieses Beispiel allerdings mit Annotations lösen. Im Prinzip ist auch das Marker-Interface nur ein Workaround um fehlende Features in der Sprache. Ein anderes bekanntes Beispiel ist das bekannte Konstanten-Interface, das seit Java5 eigentlich durch Enumerations und Static Imports ersetzt ist.

Joar, ok.
Naja, mit den Annotations und so weiter, dass ist natürlich ne elegantere Variante wohingegen sone Variante dann natürlich nur unter Java 5 nutzbar ist.

HellHorse
2005-10-27, 22:52:21
Nein, das kann so nicht sein. ;) Du vermischt da zwei Dinge.

Für das, was du tun willst, ist richtig:

if ( variable1 instanceof Class1 ) {
// ...
}

Ab Java5 gibt es bei den Generics eine Schreibweise, die etwas Ähnliches macht und mit "extends" funktioniert:

class MyClass<T extends Collection> {
// ...
}

Damit sagst du, dass für das Typargument der Klasse MyClass<T> T instanceof Collection == true gelten muss.
Nein
ArrayList instanceof Collection
kompiliert nicht.


Jetzt muss ich noch erwähnen: Wann immer du "instanceof" brauchst, solltest du dir überlegen, ob du nicht gerade an einem Workaround um einen Design-Fehler werkelst.
Stimmt eigentlich und Trap hat Recht. Dummerweise kann man die Systemklassen von Java nicht ändern und so kann man in String oder Date halt nicht eben mal schnell ein Interface implementieren. Aber classextensions sind ja so pöse.

Und an machen Orten kommt man halt nicht darum rum, weil statische Typinformation verlorenging. z.B. Swing/AWT EventListener

Trap
2005-10-27, 23:45:57
Vielleicht hilft http://www-igm.univ-mlv.fr/~forax/works/sprintabout/, es passt vom Design allerdings nicht wirklich in Java. Trotzdem könnte es in manchen Fällen ganz nützlich sein.

import fr.umlv.sprintabout.VisitorGenerator;
import java.util.ArrayList;

public abstract class SumSprintabout {
public int val(String b) { return 0; }
public int val(ArrayList b) { return 4;}
public abstract int valAppropriate(Object b);

public static void main(String[] args) throws Exception {
VisitorGenerator vg = new VisitorGenerator();
SumSprintabout visitor = (SumSprintabout) vg.createVisitor(SumSprintabout.class);

System.out.println(visitor.valAppropriate(new ArrayList()));
System.out.println(visitor.valAppropriate(new String()));
}
}
Unterscheidet nach Typ ohne instanceof im Source-Code stehen zu haben.

Abnaxos
2005-10-28, 03:16:37
ArrayList instanceof Collection
kompiliert nicht.
Asche auf mein Haupt. Da habe ich doch tatsächlich, im Bestreben, die Gegebenheiten möglichst leicht verständlich darzustellen, einen kapitalen Vehler gemacht! ;) In Java kann natürlich eine Klasse niemals ein Exemplar (eine Instanz) einer anderen Klasse sein (was in anderen Sprachen nicht gelten muss).

Stimmt eigentlich und Trap hat Recht.
Nun, nachdem die eigentliche Frage darauf hinweist, dass der OP noch relativ neu im Gebiet ist, wollte ich das Statement "wenn du 'instanceof' benötigst, solltest du über Vererbung nachdenken" nicht durch weitere Verkomplizierungen wie das Visitor- oder Decorator-Pattern verschleiern, da das so grundsätzlich richtig ist und für den Anfang reicht.

Dummerweise kann man die Systemklassen von Java nicht ändern und so kann man in String oder Date halt nicht eben mal schnell ein Interface implementieren. Aber classextensions sind ja so pöse.
Ich bin mir nicht ganz sicher, ob ich die Dinger für pöse halten soll oder nicht. Wenn man keine Mixins machen kann, wird einem das häufig einen bösen Knüppel zwischen die Beine werfen, wo man gerne etwas nachhelfen würde -- es wäre doch so einfach, wenn ... Hier stellt sich auch gleich die Frage, ob Zugriffskontrolle überhaupt eher ein Werkzeug oder ein Hindernis ist. Aber wenn massive Änderungen an der dokumentierten Verhaltensweise einer Klasse einfach so ohne weiteres möglich sind, u.U. in einem von 20 am Endprodukt beteiligten Projekten gut Versteckt, dann muss ich sagen: Gottseidank erlaubt Java keine Mixins!

Verschiedenste Teil-Projekte, auf 10 und mehr Entwickler verteilt, die nur mässig miteinander kommunizieren, sind in der Java-Entwicklung die Regel. Wenn da sämtliche beteiligten Entwickler einfach so die Möglichkeit hätten, das Verhalten von String zu ändern (das schliesst die Entwickler von externen Libraries mit ein, wir reden also von weit mehr als 10, auch von solchen, deren Namen wir nichtmal kennen), dann wäre das fatal.

Nein, es ist schon OK, dass da klare Verhältnisse herrschen, auch wenn man diese häufig gerne ändern würde. :)

HellHorse
2005-10-28, 12:58:43
Vielleicht hilft http://www-igm.univ-mlv.fr/~forax/works/sprintabout/, es passt vom Design allerdings nicht wirklich in Java. Trotzdem könnte es in manchen Fällen ganz nützlich sein.
Ich weiss nicht wirklich was ich davon halten soll. Einerseits ist es cool, anderseits ist es verdammt viel Aufwand für etwas, dass in Smalltalk eine bis drei LOC ist.


In Java kann natürlich eine Klasse niemals ein Exemplar (eine Instanz) einer anderen Klasse sein (was in anderen Sprachen nicht gelten muss).
Wenn schon, dann ist eine Klasse eine Instanz einer Metaklasse
sinngemäss:
ArrayList instanceof Collection.class
resp
ArrayList.class instanceof Collection.class.getClass()

Verschiedenste Teil-Projekte, auf 10 und mehr Entwickler verteilt, die nur mässig miteinander kommunizieren, sind in der Java-Entwicklung die Regel. Wenn da sämtliche beteiligten Entwickler einfach so die Möglichkeit hätten, das Verhalten von String zu ändern (das schliesst die Entwickler von externen Libraries mit ein, wir reden also von weit mehr als 10, auch von solchen, deren Namen wir nichtmal kennen), dann wäre das fatal.

Es geht ja nicht um das Ändern von Verhalten, es geht um das Hinzufügen von Verhalten. Zum Beispiel ein #isEmtpy() in String, oder halt eben ein #accept für einen bestimmten Visitor. Ich kann wirklich nicht erkennen, was daran schelcht oder gefährlich sein soll.

Aber wenn massive Änderungen an der dokumentierten Verhaltensweise einer Klasse einfach so ohne weiteres möglich sind, u.U. in einem von 20 am Endprodukt beteiligten Projekten gut Versteckt, dann muss ich sagen: Gottseidank erlaubt Java keine Mixins!
Was haben denn bitte Mixins mit dem 'massive Änderungen an der dokumentierten Verhaltensweise einer Klasse' zu tun?

Irgend eine Möglichkeit einer Klasse bestehendes Verhalten hinzuzufügen ohne Vererbung wäre aber schon gut. Ok, Mixins sind nicht ideal, aber etwas in der Richtung von Traits oder Modules in Ruby wäre schon gut. z.B dass man in Interfaces Code schreiben darf, der auf keine Instanzvariablen zugreifft. (Natürlich braucht man dann immer noch aliasing oder eine andere Form der Konfliktauflösung)
Schreib mal eine eigene Collection oder Stream Hierarchie und du weisst, was ich meine.

Wenn man keine Mixins machen kann, wird einem das häufig einen bösen Knüppel zwischen die Beine werfen, wo man gerne etwas nachhelfen würde -- es wäre doch so einfach, wenn ... Hier stellt sich auch gleich die Frage, ob Zugriffskontrolle überhaupt eher ein Werkzeug oder ein Hindernis ist.

Feature X ist pöse weil man damit pöse Sachen machen kann ist ein vorgeschobenes Pseudoargument. Beispiel: `Temporäre Variablen sind schlecht, weil man sie t1 bis tn nennen kann, was zu unleserlichem, unverständlichen schwer zu unterhaltendem Code fürht'.
Ich sprach von classextensions und nicht von Mixins.

SGT.Hawk
2005-10-29, 01:00:16
äh...ich hab eine abstrakte Oberklasse und 2 Klassen, die von dieser ableiten, aber ich komm um ein "instanceof" bzw. um einen Typcheck trotzdem nicht herum.
Ich habe da schon wieter gedacht.Stichwort dynamisches Binden!
Wenn du in der abstrakten(muss nicht) eine gemeinsame Methode hast,die von deinen beiden Klassen geerbet werden,brauchst du die Abfrage nicht mehr, du führst die Operation aus mit dem Cast zur Oberklasse.

Senior Sanchez
2005-10-29, 01:10:03
Ich habe da schon wieter gedacht.Stichwort dynamisches Binden!
Wenn du in der abstrakten(muss nicht) eine gemeinsame Methode hast,die von deinen beiden Klassen geerbet werden,brauchst du die Abfrage nicht mehr, du führst die Operation aus mit dem Cast zur Oberklasse.

Da musst du nicht mal "casten", du kannst auf die Methode direkt zugreifen, sofern auf Basis einer der drei Klassen gearbeitet wird.
Mal ne OT-Frage: Spricht man in diesem Fall eigentlich von Cast? Weil nen Casting ist ja ne explizite Typumwandlung, aber ein Objekt das augenscheinlich auf einen Typ "umgewandelt" wird, der in der eigenen Vererbungshierarchie ist, das ist ja keine Typumwandlung, da das Objekt ja eigentlich Typ der Klasse bereits ist.
Das JDK schmeißt nach fehlerhaften Objekt-"Castings" ja gerne ne ClassCastException, aber die Frage ist, ob das halt überhaupt nen cast ist.

lol, ist jetzt etwas verwirred, aber ums mal kurz zu machen:

class A extends B

private a = new A();
((B)a).irgendetwas(); // ist das an dieser Stelle ein Cast? weil es ist ja eigentlich keine Typumwandlung

SGT.Hawk
2005-10-29, 14:53:01
Ja, ist ein Cast. In dem Fall ein UpCast. UpCast sind ja sicher.Da kann man ja keine ClassCast haben.

Abnaxos
2005-10-29, 16:30:35
Es geht ja nicht um das Ändern von Verhalten, es geht um das Hinzufügen von Verhalten. Zum Beispiel ein #isEmtpy() in String, oder halt eben ein #accept für einen bestimmten Visitor. Ich kann wirklich nicht erkennen, was daran schelcht oder gefährlich sein soll.
Daran alleine ist in der Tat nichts gefährlich (auf den ersten Blick).

Was haben denn bitte Mixins mit dem 'massive Änderungen an der dokumentierten Verhaltensweise einer Klasse' zu tun?
Es könnte ja z.B. einer auf die Idee kommen, #equals() und #hashCode() von ArrayList so zu ändern, dass #equals() dann true zurückliefert, wenn für alle Elemente "equal" sind. Vielleicht macht er das irgendwo in den BeanUtils, die ich vielleicht eigentlich gar nicht wollte, aber durch die Verwendung des Digester mit ins Projekt gewandert sind. Viel Spass beim Suchen ...

OK, das Beispiel ist etwas gesucht, aber du verstehst, was ich meine.

Auch wenn man es nur auf's hinzufügen beschränkt, kann es gefährlich werden. Nehmen wir String#isEmpty(). Irgendwas in den Jakarta Commons fügt als der Klasse String eine Methode isEmpty() hinzu, die true liefert, falls der String leer ist oder nur aus Whitespaces besteht. Nun ziehen wir noch eine weitere Bibliothek ins Projekt, die fügt String eine Methode isEmpty() hinzu, die aber gibt true zurück, wenn string.length()==0 ist. Welche Variante von #isEmpty() wird nun verwendet? Eine der beiden Libraries wird nicht mehr korrekt funktionieren.

In Objective-C musste ich jedenfalls bitter lernen, wie schnell es geht, bis man derartige Konflikte hat und eine Kategorie einfach stillschweigend eine andere verdeckt und irgendwo irgendetwas plötzlich nicht mehr funktioniert.

class A extends B

private a = new A();
((B)a).irgendetwas(); // ist das an dieser Stelle ein Cast? weil es ist ja eigentlich keine Typumwandlung
Ein Cast ist nie eine Typumwandlung, der Typ des Objekts bleibt immer gleich.

Trap
2005-10-29, 19:19:47
Auch wenn man es nur auf's hinzufügen beschränkt, kann es gefährlich werden. Nehmen wir String#isEmpty(). Irgendwas in den Jakarta Commons fügt als der Klasse String eine Methode isEmpty() hinzu, die true liefert, falls der String leer ist oder nur aus Whitespaces besteht. Nun ziehen wir noch eine weitere Bibliothek ins Projekt, die fügt String eine Methode isEmpty() hinzu, die aber gibt true zurück, wenn string.length()==0 ist. Welche Variante von #isEmpty() wird nun verwendet? Eine der beiden Libraries wird nicht mehr korrekt funktionieren.
Das ist doch nur ein Namenskonflikt. Die Lösung ist trivial: Jeder Lib einen eigenen Namensraum geben der auch für Namen gilt die über Mixins in andere Klassen hinzugefügt werden.

Bietet keine Sprache dafür eine sinnvolle Lösung (wie die von mir beschriebene)?

HellHorse
2005-10-29, 19:53:09
Mal ne OT-Frage: Spricht man in diesem Fall eigentlich von Cast? Weil nen Casting ist ja ne explizite Typumwandlung, aber ein Objekt das augenscheinlich auf einen Typ "umgewandelt" wird, der in der eigenen Vererbungshierarchie ist, das ist ja keine Typumwandlung, da das Objekt ja eigentlich Typ der Klasse bereits ist.
Das JDK schmeißt nach fehlerhaften Objekt-"Castings" ja gerne ne ClassCastException, aber die Frage ist, ob das halt überhaupt nen cast ist.
IMHO ja, die Umwandlung von Objekten ist IIRC coercion.

Es könnte ja z.B. einer auf die Idee kommen, #equals() und #hashCode() von ArrayList so zu ändern, dass #equals() dann true zurückliefert, wenn für alle Elemente "equal" sind.
Ich glaube ich weiss, wo der Typ arbeitet.
http://java.sun.com/j2se/1.5.0/docs/api/java/util/AbstractList.html#equals(java.lang.Object)
http://java.sun.com/j2se/1.5.0/docs/api/java/util/List.html#hashCode()

Was wir hier diskutieren, das hinzufügen von Methoden zu einer Klasse, welche in ein anderes Paket/einen anderen Namespace gehören, kenne ich unter dem Namen classextensions. Unter Mixins verstehe ich Klassen, welche per Superklasse parametrisierbar sind. Das eine hat mit dem anderen nix zu tun.

@Trap
Das von dir beschriebene Konzept kenne ich unter dem Namen classboxes. Damit lassen sich lustige Sachen machen wie Klassen lokal neu definieren. Gibt afaik Implementationen für Squeak und Java.

Abnaxos
2005-10-30, 02:51:13
Was wir hier diskutieren, das hinzufügen von Methoden zu einer Klasse, welche in ein anderes Paket/einen anderen Namespace gehören, kenne ich unter dem Namen classextensions. Unter Mixins verstehe ich Klassen, welche per Superklasse parametrisierbar sind. Das eine hat mit dem anderen nix zu tun.
OK, du redest also von dem Ding, das in C# 2.0 zu finden sein wird. Ja, das scheint mir eine vernünftige Lösung, bei der ich ernsthafte Schwierigkeiten kriege, Beispiele zu finden, die belegen sollen, dass es eine schlechte Idee sei. :biggrin:

Google hat halt zu "Class Extension" nichts vernünftiges ausgespuckt und der Hinweis auf C# ist in diesem Thread auch nirgends gefallen ... ;)

HellHorse
2005-10-30, 08:47:05
OK, du redest also von dem Ding, das in C# 2.0 zu finden sein wird.
3.0
Eigentlich ist es uralt (Smalltalk kennt es vermutlich länger, als ich schon auf der Welt bin) und kommt nicht von Micro$~1, aber das überrascht uns ja nicht wirklich.
Google hat halt zu "Class Extension" nichts vernünftiges ausgespuckt
http://www.google.ch/search?q=class+extension+smalltalk