# Vererbung ohne Basisklasse zu kennen



## KuhTee (3. Apr 2012)

Hallo,


also ich denke, mein Problem welches ich habe lässt sich in Java nicht vernünftig lösen (und ich werd jetzt mal nicht über Java herziehen  ), aber vielleicht hat ja jemand eine gute Idee für mich.

Sachlage:

Ich habe 2 Klassen A und B, welche beide von einer Basisklasse erben, ansonsten aber keine Beziehung zueinander haben. Im konkreten Fall geht es um ListView und ChoiceBox aus JavaFx 2.0. Nun habe ich eine Klasse C geschrieben, welche von Klasse A erbt und erweiterte Funktionalität zur Verfügung stellt. Nach kurzer Zeit habe ich gemerkt "Hey, für Klasse B brauche ich genau das selbe". Und hier fängt der Ärger an: Es ist tatsächlich absolut das selbe. Ausser dem Namen der Klasse und dem Namen der Klasse von dem geerbt wird gibt es null Unterschied. In beiden Fällen werden nur Methoden aufgerufen, die in beiden Klassen identisch vorhanden sind. In meiner Naivität und von C++ verwöhnt dachte ich mir "hey, machst du einfach:"

```
class A<T> extends T
```
Aber das geht ja in Java nicht.
Wichtig ist, dass die abgeleitete Klasse C wirklich auch den Typ A bzw B (oder die Elternklasse von A oder B, im konkreten Fall "Control") hat, ein einfacher Wrapper tuts also nicht.

Um es vielleicht zu veranschaulichen, so würde ich das in C++ lösen:

```
class A
{
public:
    void doIt() { //many code here }
};

class B
{
public:
    void doIt() { //other code here }
};

template<typename T>
class C : public T //C inherits T
{
public:
    void run() { this->doIt(); } //call A or B
};

C<A>* c1 = new C<A>();
c1->run(); //calls A::doIt()

C<B>* c2 = new C<B>();
c2->run(); //calls B::doIt()
```

Den Aufruf der Methode könnte ich über Reflection lösen, das wäre auch noch ok. Bleibt noch das Problem, dass die abgeleitete Klasse C eben ein A, oder aber ein B können sein muss. Das geht so in Java nicht, ist mir klar. Hat ein Java-Crack vielleicht aber eine tricky Idee wie ich die Codeduplizierung vermeiden könnte? Im Endeffekt möchte ich einer Klasse zusätzliche Funktionalität "unterschieben", ohne die Klasse konkret kennen zu müssen.

Danke schonmal im Voraus


----------



## Network (4. Apr 2012)

Du kannst die aufgerufenen Methoden static machen (Nachbarthread im Forum derzeit) was aber vielen hier nicht in die Leitung passt 
Jedem das Seine. 
Auf jedenfall kannst du als static definierte Methoden überall aufrufen.

Oder du verwendest DI

Gruß
Network

PS: Wobei ich dein Problem nicht ganz verstehe! Wieso funktioniert bei dir extends jetzt nochmal genau nicht? Bzw. warum nimmst du es an?
PPS: Du bist von C++ verwöhnt?! Ich bin von C auf Java zu C++ zu Java... Ich seh zwischen C++ und Java keinen Unterschied  Ein paar, aber nicht viele ^^


----------



## mohrenkopf (4. Apr 2012)

Also vielleicht habe ich das Problem mit dem zusätzlichen Code nicht richtig verstanden, aber das hier sieht mir doch so ähnlich aus wie dein C++ Code:

[Java]

class Root{}

class A extends Root
{
    public void doIt()
    {
        System.out.println( "Ich bin ein A" );
    }
}

class B extends Root
{
    public void doIt()
    {
        System.out.println( "Ich bin B" );
    }
}

//optional:
interface Run{ public void run();}

class Aplus extends A implements Run
{
    public void run()
    {
        super.doIt();
    }
}


class Bplus extends B implements Run
{

    public void run()
    {
        super.doIt();
    }

}

public class Main
{
    public static void main( String[] args )
    {
        Run a = new Aplus();
        Run b = new Bplus();

        a.run();  //A.doIt()
        b.run();  //B.doIt()
    }
}

[/Java]


----------



## KuhTee (4. Apr 2012)

mohrenkopf hat gesagt.:


> Also vielleicht habe ich das Problem mit dem zusätzlichen Code nicht richtig verstanden, aber das hier sieht mir doch so ähnlich aus wie dein C++ Code: [...]


Na, nicht wirklich. Dein Code veranschaulicht genau mein Problem: Du implementierst Aplus und Bplus, obwohl beide bis auf den Namen und die Basisklasse absolut identisch sind. Das ist bei 5 Zeilen Code vielleicht nicht so schlimm, bei 50 Zeilen dann aber schon.



Network hat gesagt.:


> Du kannst die aufgerufenen Methoden static machen (Nachbarthread im Forum derzeit) was aber vielen hier nicht in die Leitung passt
> [...]
> Oder du verwendest DI


Tut mir leid, aber ich sehe nicht so recht, wie mir das weiterhelfen würde?



Network hat gesagt.:


> Wieso funktioniert bei dir extends jetzt nochmal genau nicht? Bzw. warum nimmst du es an?


Das geht nicht:

```
class Foo<T> extends T
```
Sagte mir zumindest Netbeans. Hat Netbeans denn gelogen?



Network hat gesagt.:


> PPS: Du bist von C++ verwöhnt?! Ich bin von C auf Java zu C++ zu Java... Ich seh zwischen C++ und Java keinen Unterschied  Ein paar, aber nicht viele ^^


Es sind besonders die Generics, die mich immer wieder Haare raufen lassen


----------



## Spacerat (4. Apr 2012)

...was in Java aber geht:[c]Extension<T extends Control>[/c].

```
public class ControlExtension<T extends Control>
{
  private final T parent;

  public ControlExtension(T parent)
  {
    this.parent = parent;
  }
}
```
T kann nun jede Klasse werden, die Control implementiert oder erweitert. Innerhalb der ControlExtension können Methoden von [c](Control) parent[/c] aufgerufen oder aber auch an diese delegiert werden. Die Methoden von [c](A) parent[/c] oder [c](B) parent[/c] stehen aber leider nicht zur Verfügung.


----------



## mohrenkopf (4. Apr 2012)

KuhTee hat gesagt.:


> Na, nicht wirklich. Dein Code veranschaulicht genau mein Problem: Du implementierst Aplus und Bplus, obwohl beide bis auf den Namen und die Basisklasse absolut identisch sind. Das ist bei 5 Zeilen Code vielleicht nicht so schlimm, bei 50 Zeilen dann aber schon.



In deiner Demo rufst du doch auch nur die doIt von entweder A oder B auf. Wo stehen denn bei dir die 50 Zeilen Code?


----------



## turmaline (4. Apr 2012)

hm vielleicht so:


```
public interface IRun 
{
	void doIt();
}
```


```
public class C implements IRun
{
	private Base b;
	
	public C(Base b) 
	{
		this.b = b;
	}

    public void doIt()
    {
        // do something for all base classes, use b
    }

}
```


```
public class Test 
{

    /**
     * @param args
     */
    public static void main(String[] args) 
    {
        Base a = new Base();
        Base b = new Base();
        
        IRun cForA = new C(a);
        IRun cForB = new C(b);
        
        cForA.doIt();
        cForB.doIt();
    }

}
```

oder so..


----------



## turmaline (4. Apr 2012)

option2



turmaline hat gesagt.:


> ```
> public interface IRun
> {
> void doIt(Base b);
> ...


----------



## turmaline (4. Apr 2012)

hab grad den Satz bei Dir gelesen:

Wichtig ist, dass die abgeleitete Klasse C wirklich auch den Typ A bzw B (oder die Elternklasse von A oder B, im konkreten Fall "Control") hat, ein einfacher Wrapper tuts also nicht.

Na gut: dann vieleicht so:


```
public class C extends Base
{

     public void doIt(Base other)
     {
     }

}
```


```
Base a = new A();
Base b = new B();

Base c = new C();
c.doIt(a);
c.doIt(b);
```

weiß nicht ob das zu Deinem Anwendungsfall passt und ich gebe zu es ist etwas getrickst... :autsch:


----------



## KuhTee (4. Apr 2012)

Spacerat hat gesagt.:


> T kann nun jede Klasse werden, die Control implementiert oder erweitert. Innerhalb der ControlExtension können Methoden von [c](Control) parent[/c] aufgerufen oder aber auch an diese delegiert werden.


ControlExtension ist aber nicht vom Typ Control, und das ist nötig um das Objekt anderen JavaFX Node Objekten als Child zu geben.



mohrenkopf hat gesagt.:


> In deiner Demo rufst du doch auch nur die doIt von entweder A oder B auf. Wo stehen denn bei dir die 50 Zeilen Code?


Das ist doch nur ein Beispiel 

@turmaline: Danke für die Vorschläge, aber bringt mich leider auch nicht voran. Vielleicht am ehesten noch die dritte Variante, eine Art "Wrapper Control". Schick ist das aber nicht.


Danke an alle


----------



## Spacerat (4. Apr 2012)

KuhTee hat gesagt.:


> ControlExtension ist aber nicht vom Typ Control, und das ist nötig um das Objekt anderen JavaFX Node Objekten als Child zu geben.


Oh sorry...

```
public class ControlExtension<T extends Control>
extends Control
{
  //code, code, code
}
```
... jetzt schon.


----------



## mephi (5. Apr 2012)

Kannst du nicht einfach für die beiden Unterklassen ein Interface anbieten dass sie implementieren und die eigenltiche Logik(wenns denn wirklich genau die selbe ist) realisierst du über Komposition!?


----------



## KuhTee (7. Apr 2012)

mephi hat gesagt.:


> Kannst du nicht einfach für die beiden Unterklassen ein Interface anbieten dass sie implementieren und die eigenltiche Logik(wenns denn wirklich genau die selbe ist) realisierst du über Komposition!?


Daran habe ich auch kurz gedacht. Aber die Logik besteht aus mehreren public Methoden, die ich dann trotzdem in jeder Klasse implementieren müsste. Wirklich viel nutzen bringt das leider nicht.

Aber ich könnte natürlich nur eine einzige Methode im Interface definieren die da heisst "getControlExtension". Aber sonderlich schick ist das nicht. Ist dann ja fast wie OOP in C.


----------



## JohannisderKaeufer (7. Apr 2012)

Vererbung ohne die Basisklasse zu kennen macht wenig Sinn.

Entweder werden Methoden überschrieben, dann muß man aber zumindest wissen, das es sie gibt.

Oder man nutzt Methoden einer Superklasse, auch dann sollte man von deren Existenz Kenntnis haben.

Was geht ist folgendes

```
public class A<T extends BaseInterface> implements BaseInterface{
  private final T base;
  public (T base){
    this.base = base;
  }

  @Override
  public void methodFromInterface(){
    base.methodFromInterface();
  }

  public void anotherMethod(){
    base.methodFromInterface();
  }

  public T getInstance(){
    return base;
  }
}
```

Methoden die in der BaseKlasse sind, können dann über 
new A(new SomeBase()).getInstance().specialBaseMethod(); aufgerufen werden.

Nehmen wir nur einmal an, in einer unbekannten basisklasse gibt es eine unbekannte Methode die du zufälligerweise auch in deiner Erbenden Klasse gleich benannt hast, aber eigentlich garnicht vorhast, diese zu überschreiben. Dann hast du ein Problem, vor dem dich ein Compiler nicht warnen kann, da die Basisklasse ja unbekannt sein soll.


----------



## KuhTee (8. Apr 2012)

Ich sagte eingangs doch, dass mir durchaus klar ist, dass das in Java so leider nicht geht.
Was konkret ich tun wollte hatte ich ja nun inzwischen erläutert denke ich. In anderen Sprachen (C++, JavaScript, mal als Beispiele) ist das was ich machen möchte recht einfach realisierbar. Ich wollte ja nur wissen, ob jemand für Java einen ähnlichen Vorschlag machen kann.

Leider bietet Java (derzeit) nicht die nötigen Funktionalitäten dafür.


----------



## Nightmares (8. Apr 2012)

Meine Lösung für Java in solchen Fällen sieht so aus:

Klassen A und B die beide Zeugs von C brauchen aber nicht von C erben können:
Ein Objekt von C in A und B.
Ist zwar auch nicht schön, aber besser als das ganze zwei mal zu schreiben.


Wenn C etwas von den Klassen A oder B braucht, dass sie benutzen sollen muss eine Akstrakte Klasse oder ein Interface her das A und B implementieren und eine Referenz von A oder B muss an den C Konstruktor weiter gegeben werden.

also z.B. dann so:


```
class C {
	
	CReqs	creqs;
	
	public C(CReqs creqs) {
		this.creqs = creqs;
	}
	
	public void doSomething() {
		this.creqs.someMethodThatCNeeds();
	}
	
}

abstract class CReqs {
	
	public abstract Object someMethodThatCNeeds();
}

class B extends CReqs{
	
	private C	c;
	
	public B() {
		this.c = new C(this);
	}
	
	public C getC() {
		return this.c;
	}
	
	@Override
	public Object someMethodThatCNeeds() {
		return null;
		// was halt dann gebraucht wird
		// oder direkt als feld deklarieren und drauf zugreifen was man halt lieber mag
	}
	
}


class Test {
	 // und dann später
	 public static void main(
	   B b = new B();
	   b.getC().doSomething();
	 }
}
```

Hoffe das ist nützlich. Wenn ein "konkretes / weltechtes" Beispiel dir lieber wäre sag nochmal bescheid.


EDIT: Reflection
Ansonsten gibts noch Reflection. Das geht sogar so weit, das man Klassen "on-the-fly" bauen kann. Dh. Methoden aussuchen von anderen Klassen und in eine neue einfügen (Kein Witz, das geht ist aber nicht zu empfehlen, weil man nie wirklich alles Überschauen kann und das ganze einem schnell um die Ohren fliegt. Hat also nichts in Production Code zu suchen, meiner Meinung nach. Aber das muss jeder selber wissen. Hierzu siehe ASM und/oder BCEL)


----------



## Spacerat (8. Apr 2012)

KuhTee hat gesagt.:


> Leider bietet Java (derzeit) nicht die nötigen Funktionalitäten dafür.


Hast du meinen 2. Post überlesen? Pack beim 1. noch dieses [c]extends Control[/c] vor die Klassendefinition und du hast deine ControlExtension, denke ich.


----------



## Nightmares (8. Apr 2012)

Bei deinem Beispiel sehe ich nicht wie ich von verschiedenen Klassen verschiedene Dinge an C Methoden weiter gebe je nach Bedarf ohne die C Logik zu verändern. 

Edit: Und deine Lösung Funktioniert soweit ich das sehe nicht für mehrfach Vererbung (dh. wenn eine Klasse dann von 4 Dingen erweiter werden soll z.B.)


----------



## Spacerat (8. Apr 2012)

Ok, mal zusammengefasst:

```
public class ControlExtension<T extends Control>
extends Control
{
  private final T parent

  public ControlExtension(T parent)
  {
    if(parent == null) {
      throw new NullPointerException();
    }
    this.parent = parent;
  }

  public void run()
  {
    doIt();
  }

  public void doIt()
  {
    parent.doIt();
  }
}
```
Dabei gehe ich mal davon aus, dass [c]doIt()[/c] eine Methode von Control ist, die überschrieben werden muss. Sonst müsste man A und B nämlich vorher noch in eine Klasse Wrappen, welche ein Interface implementiert, dass die gewünschte, gemeinsam genutze Funktionalität bietet.

```
public interface DoItInterface
{
  public void doIt();
}

public class DoItWrapper
extends Control
implements DoItInterface
{
  private Control parent;
  private A objectA;
  private B objectB;

  public DoItWrapper(Control parent)
  {
    if(parent == null) {
      throw new NullPointerException();
    }
    this.parent = parent;
  }

  public DoItWrapper(A parent)
  {
    if(parent == null) {
      throw new NullPointerException();
    }
    this.objectA = parent;
  }

  public DoItWrapper(B parent)
  {
    if(parent == null) {
      throw new NullPointerException();
    }
    this.objectB = parent;
  }

  public void doIt()
  {
    if(objectA != null) {
      objectA.doIt();
    } else if(objectB != null) {
      objectB.doIt();
    } else {
      parent.doit();
    }
  }
}

public class ControlExtension<T extends DoItWrapper>
extends DoItWrapper
{
  public ControlExtension(T parent)
  {
    super(parent);
  }

  public void run()
  {
    /*super.*/doIt(); // hier wird über die geerbte doIt()-Methode parent.doIt() aufgerufen
  }
}
```
Letzteres ist natürlich sehr unfein.


----------



## mephi (10. Apr 2012)

KuhTee hat gesagt.:


> Daran habe ich auch kurz gedacht. Aber die Logik besteht aus mehreren public Methoden, die ich dann trotzdem in jeder Klasse implementieren müsste. Wirklich viel nutzen bringt das leider nicht.
> 
> Aber ich könnte natürlich nur eine einzige Methode im Interface definieren die da heisst "getControlExtension". Aber sonderlich schick ist das nicht. Ist dann ja fast wie OOP in C.



_"Favor object composition over class inheritance" - GoF_
Ich würde mein Modell nicht durch abstruse Vererbungen "hinwurschteln"


----------



## fastjack (10. Apr 2012)

Bevor man da drauf losprogrammiert, sollte man sich nicht vielleicht erst einmal ansehen, was das für ein Pattern sein könnte?

Du hast im Prinzip zwei verschiedene Klassen A und B, haben die verschiedene Methoden oder gleiche Methoden? Du hast die Klasse C, die entweder A oder B benutzen soll.

Ich würde einen Mediator probieren, vielleicht auch mal einen Adapter.

Kann Klasse C in der "Sprache C" jetzt alle möglichen Methoden aufrufen, die man möchte, also auch Methoden, die A und B vielleicht beiden icht haben? Das halte ich persönlich für schlecht. Angemommen jemand steckt ein Z in C, dieses Z hat aber gar nicht die doIt()-Methode, was passiert nun? Das sind die feinen Unterschiede zwischen Java und C. Vielleicht helfen Dir auch Reflections dabei...


----------

