PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Constructor für überladene Klassen



kit
13-02-2005, 17:00
hallo allen zusammen,

ich habe folgende Frage:

Ich möchte eine abstrakte Klasse foo durch die Klasse myclass implementieren, so dass die volle Funktionalität in foo ist, jedoch eine Variable myvar spezifisch in myclass gesetzt wird.



abstract class foo {
int myvar;

foo() {
dosomething();
}

abstract void dosomething();
}

class myclass extends foo {
void dosomething() {
myvar=10;
}
}


Mein Problem ist nun, dass es nicht funktioniert: ich muss in myclass eine Konstruktor mitgeben, so dass ich folgendes habe:



class myclass extends foo {
myclass() {
super();
}

void dosomething() {
myvar=10;
}
}



Tut das not? Warum wird der Konstruktur nicht ebenfalls abgeleitet? Kann ich es irgendwie so schreiben, dass ich mir den zweiten Konstruktor, der ja doch nur super() aufruft, spare?

Ich freue mich über alle sachdienlichen Hinweise! Viele Grüße
kit

KL47
13-02-2005, 18:21
Ne, Konstruktoren werden nicht abgeleitet, soweit ich weiß. Das ginge aber in C++ ;)

nul
13-02-2005, 19:48
Das ist doch sehr gefaehrlich was du da vorhast. Wenn es funktionieren wuerde, dann wuerde die Sache sicher eine dicke Exception bringen. Spaetestens wenn du auf die noch nicht gesetzte Variable irgendwelche Operationen ausfuehren willst.
Das Attribut in der Superklasse ist so lange null, bis du es in der Subklasse setzt, wenn ich dich reichtig verstehe.
Wenn du also im Konstruktor der Superklasse eine Methode aufrufst, die mit dieser Variable arbeitet, dann ist die Variable immer noch null.
Die Methode wird ausgefuehrt, dann kommt erst die Methode der Subklasse dran und setzt den Wert fuer die Superklasse (wenn du ueberhaupt dazu kommst).

mfg nul

kit
13-02-2005, 20:08
@nul:

ich verstehe nicht, was daran gefährlich sein soll: um Deine argumentation zur vermeiden, ist die Klasse foo bzw. die Methode dosomething ja abstract. Das ist ganz simples überladen von Funktionen. Es funktioniert ja auch, nur eben mit einem aus meiner Sicht völlig überflüssigem Konstruktor, der aber auch nur den Konstruktor von foo aufruft.
Stell Dir mal eine Tabelle zum Editieren vor, die für verschiedene Aufgaben verschiedene Spalten mit verschiedenen Formatierern hat. Dann wird der Zweck evident. Ich habe nur ein einfaches Beispiel dargestellt.

@KL47:

Danke für die Bestätigung, ich hatte auch nur in Erinnerung, dass es in C++ geht. Mir ist nur der Sinn der Einschränkung in Java nicht wirklich klar. Warum kann er nicht einfach den vorhandenen Konstruktor nehmen??? Ich finde Java ziemlich "redselig"...

anda_skoa
14-02-2005, 17:38
Wenn du die Variable im Konstruktor setzen willst und jede Subklasse muss sie setzen, wäre es nicht besser du machst es im Konstruktor?



public class Foo
{
private int myvar;

protected Foo(int var)
{
myvaw = var;
}
}



public class MyClass extends Foo
{
public MyClass()
{
super(10);
}
}


Ciao,
_

kit
15-02-2005, 07:39
guten Morgen anda_skoa,

Du hast recht, ich könnte auch den gesamten Konstruktor ableiten und die Variable dort setzen oder die Variable als Parameter übergeben.

Der Grund, warum ich dass dann doch nicht mache, ist die praktische Anwendung. Es geht um eine Tabelle, welche in den Spalten nur ein Datum oder eine Währungsangabe akzeptiert. Dazu gebe ich spezielle CellRenderer und CellEditoren mit. Diese Tabelle gibt es in meinem Programm in verschiedenen Versionen: mal mit 2 Spalten (Date,Decimal), mal mit 3 Spalten (Date, Date, Decimal), mal mit 5 Spalten (Decimal, Decimal, Date, Date, Decimal) usw.

Der Konstruktor der Tabelle initialisiert nun die Tabelle und benötigt dafür zwei Arrays: eines für die CellEditoren, eines für die CellRenderer.
Möchte ich das nun als Parameter übergeben und im Konstruktor selbst setzen, nehme ich die ganze Initialisierung und die Packete javax.swing.JTable und javax.swing.table.* in die Klasse, welche nachher meine Tabellen aufrufen und anzeigen soll. Da geht mir der Überblick verloren, es ist mir zuviel Code an der falschen Stelle.

Stattdessen bevorzuge ich, die Arrays für die CellEditoren und die CellRenderer abstract zu deklarieren und dann für jede Implementierung der Tabelle mit 2,3,4,5 Spalten eine eigene Klasse abzuleiten, welche ausschließlich die Arrays setzt. Sieht nach meinem Empfinden sauber aus -- wenn der blöde Konstruktur nicht sein müsste.

Viele Grüße
kit

kit
15-02-2005, 07:45
by the way: kann ich Arrays auch nach der Deklaration mit geschweiften Klammern {} initialisieren? also statt


Object o={a, b, c};


lieber ein


Object o;
//ganz viele Zeilen Code
o={a,b,c};


Ich weiss, dass zweites Beispiel nicht geht, suche aber nach einer Möglichkeit, es genauso komfortabel zu machen.

Viele Grüße
kit

anda_skoa
15-02-2005, 14:05
Ich verstehe leider das Problem jetzt nicht mehr.

Von deinem ursprünglichen Code ausgehend ist mein Ansatz wesentlich weniger Aufwand.

Abgesehen davon das Fehlen eines Defaultkonstruktors und der Parameter im verfügbaren Konstruktor der Basisklasse die Wichtigkeit des Parameters hervorheben und auch garantieren, dass etwas gesetzt wurde.

Worin besteht aus deiner Sicht der Unterschied zwischen Überschreiben einer Methode und Überschreiben eines Konstruktors?

Ciao,
_

kit
15-02-2005, 14:36
Ich verstehe leider das Problem jetzt nicht mehr.
;o) habe ich befürchtet, ist wohl auch mit Code besser zur erklären, als mit worten. ich habe nur leider das Teil nicht hier.



Von deinem ursprünglichen Code ausgehend ist mein Ansatz wesentlich weniger Aufwand.

ja, dass ist richtig. das Beispiel ist klar vereinfacht, um die Struktur (nicht den Sinn) meines Problems klar zu machen. ich hänge heute Abend mal den richtigen Code an.



Worin besteht aus deiner Sicht der Unterschied zwischen Überschreiben einer Methode und Überschreiben eines Konstruktors?


gehen wir mal davon aus, dass ich eine Methode habe, die aus einem festen, generischen Teil A und einem variablen, veränderlichen Teil B besteht. dann möchte ich natürlich A nur einmal schreiben und B muss ich für jede Ableitung einzeln schreiben.

Ich wollte A als Konstruktor umsetzen, der dann B aufruft, so dass ich nur B schreiben muss.

Alternativ könnte ich nun entweder A als Konstruktor schreiben, der B als Parameter bekommt. Das will ich aber nicht, weil aus meiner Sicht schlechte Struktur.

Zuletzt kann ich A als Methode schreiben und B jeweils als Konstruktor, der dann A aufruft. Dass kommt dem, was ich möchte am nächsten, aht aber zwei Nachteile:
1) Ich muss die Methode A in jedem abgeleiteten Konstruktor explizit aufrufen --> rendundant, fehleranfällig, häßlich
2) Ich muss im Konstruktur Variable und Objekte initialisieren, was eigentlich zum fixen Teil A gehört --> wieder Redundanz.

Somit werde ich wohl dabei bleiben, dass ich den überflüssigen Konstruktor umsetze, der super() aufruft.

Ist nur eben schade, dass es mit JAVA nicht geht.



import javax.swing.JTable;
import javax.swing.table.*;
import java.util.Iterator;
import java.util.Vector;
import java.util.Date;
import java.math.BigDecimal;
import javax.swing.border.EmptyBorder;

abstract class RuleTable extends JTable {
GlobalProperties GP;
DefaultTableModel dtm;

String[] hs;
TableCellEditor[] tce;
DefaultTableCellRenderer[] dtcr;

RuleTable(GlobalProperties GP) {
EmptyBorder EB=new EmptyBorder(0,0,0,0);
dtm = new DefaultTableModel();
this.GP=GP;

initRuleTableColumns();
for (int i=0; i<hs.length; i++) {
dtm.addColumn( GP.localizeStr(hs[i]) );
}
setModel(dtm);
for (int i=0; i<hs.length; i++) {
dtcr[i].setBorder(EB);
getColumnModel().getColumn(i).setCellRenderer( dtcr[i] );
getColumnModel().getColumn(i).setCellEditor( tce[i] );
}
}

abstract void initRuleTableColumns();

public void addRow() {
Object[] o={new Date(), new BigDecimal(0d)};
dtm.addRow(o);
}

public void removeRow() {
int r=getSelectedRow();
dtm.removeRow(r);
if (r>1) {
setRowSelectionInterval(r,r);
} else {
clearSelection();
}
}
}


class CapitalRuleTable extends RuleTable {
CapitalRuleTable(GlobalProperties GP) {
super(GP);
}

void initRuleTableColumns() {
String[] hs = {"i18n_Valuta","i18n_Amount"}; this.hs=hs;
TableCellEditor[] tce={new DateTextField(GP), new DecimalTextField(GP)}; this.tce=tce;
DefaultTableCellRenderer[] dtcr={new DateCellRenderer(), new DecimalCellRenderer("#,##0.00")}; this.dtcr=dtcr;
}
}

anda_skoa
17-02-2005, 17:46
Hmm, ich verstehe das Problem jetzt wesentlich besser.

Allerdings ist die Lösung noch nicht sehr gut, die Basisklasse nimmt einfach an, dass die Subklasse was richtiges macht, die API selbst erfordert das nicht, d.h. eine Subklasse könnte die abtrakte Methode auch einfach leer implementieren.

Besser wären für alle drei Arrays jeweils eine Methode mit entsprechendem Rückgabewert, das ist schon mal ein guter Hinweis.
Theoretisch brauchst du die drei Arrays dann nur als lokale Variablen im Basisklassenkonstrukto, außer du brauchst die konkreten Arrays noch später.

Vermutlich gibt es noch sauberere Lösungen, vielleicht eine Klasse die die korrekten Arrays erzeugen kann, die Subklasse müsste dann nur einen Instanz davon an super() weitergeben.

Ciao,
_

nul
17-02-2005, 18:50
by the way: kann ich Arrays auch nach der Deklaration mit geschweiften Klammern {} initialisieren? also statt


Object o={a, b, c};


lieber ein


Object o;
//ganz viele Zeilen Code
o={a,b,c};


Ich weiss, dass zweites Beispiel nicht geht, suche aber nach einer Möglichkeit, es genauso komfortabel zu machen.

Viele Grüße
kit


Jup, mit

Object[] o;
o = new Object[]{...};
Sollte glaub ich so gehn, bin mir nicht 100% sicher

kit
17-02-2005, 19:22
Allerdings ist die Lösung noch nicht sehr gut, die Basisklasse nimmt einfach an, dass die Subklasse was richtiges macht, die API selbst erfordert das nicht, d.h. eine Subklasse könnte die abtrakte Methode auch einfach leer implementieren.


Ja, das ist prinzipiell richtig. Aber ich bin überrascht, dass das ein Problem ist, denn letztendlich darf ich nicht erwarten, dass etwas funktioniert, wenn ich es nicht schreibe. Ich könnte die Initialisierung auch in der Basisklasse vergessen, da sehe ich keinen Unterschied...



Besser wären für alle drei Arrays jeweils eine Methode mit entsprechendem Rückgabewert, das ist schon mal ein guter Hinweis.

Ok, aber mir ist noch nicht klar, was ich dann gewinne: könnte ich wie oben nicht auch vergessen, etwas in die Arrays zu schreiben und dann leere Arrays zurückgeben???



Theoretisch brauchst du die drei Arrays dann nur als lokale Variablen im Basisklassenkonstrukto, außer du brauchst die konkreten Arrays noch später.

sehr gutes Argument, das verstehe ich.



Vermutlich gibt es noch sauberere Lösungen, vielleicht eine Klasse die die korrekten Arrays erzeugen kann, die Subklasse müsste dann nur einen Instanz davon an super() weitergeben.

Das verstehe ich auch, allerdings halte ich es dann für theoretisch sauber, praktisch aber für unübersichtlicher. das ist gewiss Ansichtssache.

vielen Dank für Deine Hilfe und Kritik und viele Grüße
kit

kit
17-02-2005, 19:27
Jup, mit

Object[] o;
o = new Object[]{...};
Sollte glaub ich so gehn, bin mir nicht 100% sicher

naja, gemäß meinem Beispiel habe ich versucht:


hs = String[]{"i18n_Valuta","i18n_Amount"};


und erhalte:


compile:
[javac] Compiling 2 source files to /home/are/src/buchungsplan/bin
[javac] /home/are/src/buchungsplan/src/RuleTable.java:59: '.class' expected
[javac] hs = String[]{"i18n_Valuta","i18n_Amount"};
[javac] ^
[javac] 1 error


scheint also nicht zu funktionieren. aber der Versuch war es wert.
Viele Grüße
kit

anda_skoa
19-02-2005, 19:02
Ja, das ist prinzipiell richtig. Aber ich bin überrascht, dass das ein Problem ist, denn letztendlich darf ich nicht erwarten, dass etwas funktioniert, wenn ich es nicht schreibe. Ich könnte die Initialisierung auch in der Basisklasse vergessen, da sehe ich keinen Unterschied...

Die eine Methode bietet selbst wenig Hinweise, welche Variablen eine Implementierung zu setzen jat.



Ok, aber mir ist noch nicht klar, was ich dann gewinne: könnte ich wie oben nicht auch vergessen, etwas in die Arrays zu schreiben und dann leere Arrays zurückgeben???

Klarer Fall, aber du könntest zB für jede der drei Methoden den Rückgabewert prüfen und im Bedarfsfall eine Exception werfen.
Der wesentliche Vorteil ist aber der Hinweise welche drei Dinge von der Subklasse erwartet werden.



Das verstehe ich auch, allerdings halte ich es dann für theoretisch sauber, praktisch aber für unübersichtlicher. das ist gewiss Ansichtssache.

Ist praktisch ähnlich wie die Sache mit dem TableModel. Sicher auf den ersten Blick mehr Aufwand alsvon JTable direkt abzuleiten und ein paar Methoden zu implementieren, aber wesentlich besser abstrahiert und daher universeller einsetzbar.

Ciao,
_

kit
20-02-2005, 11:44
hi,

ich verstehe Deine argumente im Hinblick auf exportierte API's. Und Du hast recht, denn in Deinen Beispielen hast Du explicit public deklariert.
Allerdings ging ich immer von einer private-Deklaration aus, wollte also meine Tabelle nicht zugänglich machen. Ich muss also wissen, welche Variablen ich zu setzen habe. Wenn ich es nicht weiss, sagt es mir spätestens der Compiler ;o) Deshalb bevorzuge ich hier weniger, schlanken Code.

Viele Grüße
kit