Datentypen Generischen Typ instanzieren

Holger_Hessling

Aktives Mitglied
Hallo Forum,

ich möchte eine Liste von Objekten verwalten, zu denen zusätzliche Daten gespeichert werden. Drum hab ich das in eine eigene Klasse gepackt und diese generisch gemacht. Das sieht jetzt mal so aus:
Code:
public class DOList<T extends DOElement> {

   private DOTauchgang tg;
   private List<T> list = new ArrayList<>();
   private Integer activeIndex;
   private List<DOListListener> listener = new ArrayList<>();

   public DOList(DOTauchgang tg, String label) {
     super(label);
     this.tg = tg;
     activeIndex = null;
   }
}
Der Datentyp T ist einer aus fünf möglichen, die jeweils von DOElement abgeleitet sind. Somit sind jetzt nur Listen mit diesen fünf Typen möglich.
Was ich jetzt leider nicht hinbekomme, daß ich in einer Klassenmethode (z. B. im Konstruktor) ein neues Objekt vom Typ T instanziiere. Also sowas zum Beispiel:
Code:
    T newElement = new T();
    list.add(newElement);
Ich würde erwarten, daß eines der fünf möglichen Objekte erzeugt wird, auf Grundlage des Typs T, mit dem die DOList instanziiert wurde. Aber das geht nicht. Mache ich etwas falsch oder ist das prinzipiell nicht möglich?

VG Holger
 
Das funktioniert generell nicht, man nicht davon ausgehen kann, dass alle von DOElement abgeleiteten Klassen einen Standardkonstruktor besitzen.

und welches denn? Woher soll Java wissen, welches der 5 Typen du willst?

Das ist eigentlich für Java klar, der Typ steht ja im Typparameter T.

P.S: Ich glaube in Java 9 soll es demnächst Lösungen für dieses Problem geben, bin mir da jetzt aber nicht ganz sicher.
 
Nein, die Liste hat ja den generischen Typenparameter und der Liste will er ein Objekt hinzufügen, ausserhalb von der Liste ist aber T aber unbekannt.

Ausser ich verstehe die Codeschnippsel falsch
 
Also ich frage mich sowieso wieso er das generisch macht. Wenn 5 Klassen DOElement extenden, kann ich doch alle Klassen als DOElement instanziieren und in die Liste packen. (List<DOElement>).
 
Also ich frage mich sowieso wieso er das generisch macht. Wenn 5 Klassen DOElement extenden, kann ich doch alle Klassen als DOElement instanziieren und in die Liste packen. (List<DOElement>).

Weil es etwas anderes ist:

List<DOElement> : Liste von beliebigen DOElement Typen
List<? extends DOElement > : Liste von einem bestimmten DOElement Typ
 
Um der Problemlösungmal ein wenig näher zu kommen:
Ich mache das dann so, dass ich entweder im Konstruktor eine Class-Referenz auf den konkreten Typ übergebe, oder der Typ selbst bekommt eine Factory-Methode, bzw man übergibt eine Factory-Klasse.

Der einfachste Fall sieht dann etwa so aus
Java:
public class Foo<T> {
    private Class<T> clazz;

    public Foo(Class<T> clazz){
        this.clazz = clazz;
    }

    public T createInstance() throws InstantiationException, IllegalAccessException {
        return clazz.newInstance();
    }
}
 
P.S: Ich glaube in Java 9 soll es demnächst Lösungen für dieses Problem geben, bin mir da jetzt aber nicht ganz sicher.
Nö, reified types werden bestimmt nicht kommen.

Ich würde das eher so machen (mit Java 8) einen Supplier<T> übergeben und dann einfach eine Instanz kreieren lassen, ohne unnötige Reflections:
Java:
public final class Factory<T> {
  private final Supplier<T> generator;
  
  public Factory(Supplier<T> generator) {
    Objects.requireNonNull(generator);
    this.generator = generator;
  }

  public T createInstance() {
    return generator.get();
  }
}
Aufruf dann mit einer (Sub-)Klasse:
Java:
Factory<DOElement> factory = new Factory<>(SubDOElement::new);
 
Hallo Leute,

wow, da hab ich ja eine Diskussion angeregt 🙂 Leider war ich gestern Abend verhindert und lese Eure Beiträge erst heute. Offenbar ist dieses "Problem" durchaus bekannt und offenbar gibt es dafür (sehr spezielle) Lösungen. Die angeregten Lösungen muß ich mir heute Abend mal in Ruhe ansehen und versuchen zu verstehen (bin halt doch Anfänger 😉).

Vielen Dank für Eure Antworten!!!!!

VG Holger
 
Hallo Forum,

durch Eure guten Beiträge konnte ich mein Problem lösen. Angeregt durch die Beispiele mit den Java Reflections habe ich ein wenig darüber gelesen und hab meine eigenen Listen-Klasse jetzt so angelegt:
Code:
public class DOList<T extends DOElement> extends DataObject {

   private Class<T> listType;
   private DOTauchgang tg;
   private List<T> list = new ArrayList<>();
   private List<DOListListener> listener = new ArrayList<>();

   public DOList(Class<T> listType, DOTauchgang tg, String label) {
     super(label);
     this.listType = listType;
     this.tg = tg;
   }

   private T newInstance() {

     Constructor<T> constructor = null;
     try {
       constructor = listType.getConstructor(String.class);
     } catch (NoSuchMethodException | SecurityException e) {
       e.printStackTrace();
     }

     T newElement = null;
     try {
       newElement = constructor.newInstance("new '" + listType.getSimpleName() + "'");
     } catch (InstantiationException | IllegalAccessException | IllegalArgumentException
         | InvocationTargetException e) {
       e.printStackTrace();
     }
     return newElement;
   }

  public void newElement() {

     T newElement = newInstance();
     newElement.setList(this);
     setAllInactive();
     newElement.setActive();
     list.add(newElement);

     for (DOListListener l : listener) {
       l.elementAdded(this.tg, this, newElement);
     }

   }
Mit dieser Konstruktion ist es möglich, einer Instanz der Klasse DOList<T extends DOElement> über ihre Methode newElement() anzuweisen, sich selbst ein neues Element zu erzeugen und der eigenen Liste hinzuzufügen. Und das auf der Basis des Typs <T> beim instanziieren der DOList. 🙂

Ich habe durch Euch wieder viel gelernt. Vielen Dank nochmal!!!!

VG Holger
 
Ist jetzt aber keine Lösung auf die du stolz sein solltest! Mittels Reflections ein Objekt erzeugen ist hier komplett übertrieben, denn du kennst ja den Konstruktor!
Hier wäre eine Factory eindeutig die bessere Wahl.
 
Hm, war eigentlich schon stolz, wieder etwas gelernt zu haben 🙂 Hättest Du Lust, mir Deinen Vorschlag etwas deutlicher zu machen? Deinen Code aus Deiner vorletzten Antwort verstehe ich leider nicht.

VG Holger
 
Ist jetzt zwar ein Sinnlosbeispiel (ähnlich gehalten wie deine Klassen):
Java:
public class Test {
  public static void main(String... args) {
    TypeList<SubType> list = new TypeList<>(new TypeFactory<SubType>() {
      @Override
      public SubType create() {
        return new SubType(SubType.class.getSimpleName());
      }
    });
    list.newElement();
  }
 
}

interface TypeFactory<T extends Type> {
  T create();
}

abstract class Type {
  private String instanceName;
 
  public Type(String instanceName) {
    this.instanceName = instanceName;
  }
 
  @Override
  public String toString() {
    return instanceName;
  }
}

class SubType extends Type {
 
  public SubType(String instanceName) {
    super(instanceName);
  }
}

class TypeList<T extends Type> {
  private TypeFactory<T> factory;
 
  public TypeList(TypeFactory<T> factory) {
    this.factory = factory;
  }
 
  public void newElement() {
    T element = factory.create();
    System.out.println(element);
  }
}
EDIT: Mit Java 8 geht das auch so:
Java:
public class Test {
  public static void main(String... args) {
    TypeList<SubType> list = new TypeList<>(() -> new SubType(SubType.class.getSimpleName()));
    list.newElement();
  }
}
 
Hi Flown, (wie ist Dein Name? 🙂)

vielen Dank für Deine Mühe!!!! Das Beispiel funktioniert. Ich hab's mal auf meine Klassennamen verändert und um meine Klassen erweitert. Diese Konstruktion hätte ich noch lange nicht selbst ausbrüten können. Mit dem Verstehen Deines Beispiels habe ich auch meine Problemchen 🙄 Aber anhand Deines Beispiels werde ich mich durchhangeln!!!! Irgendwann wird der Groschen schon fallen.

Dein EDIT habe ich auch ausprobiert. Das ist das Thema Lamdas und funktionale Interfaces. Die durchblicke ich ebnefalls noch nicht. Aber Dein Beispiel funktioniert auch nicht. Leider! Den Fehler kann ICH natürlcih noch nicht finden 😀😀

VG Holger
 
Hallo Florian,

so langsam durchblicke ich auch dieses Designpattern mit der Factory. Findet man ja einiges im Netz. Schöne Sache. Jetzt hab ich sogar einen Einstieg in Lambda-Expressions gehabt und setze sie in meinem Programme in.
(jetzt bin ich wieder stolz 😉)

VG Holger
 

Zurück
Oben