# Generic Typ zur Laufzeit



## Dragonfire (8. Nov 2009)

Gibt es irgendeine Möglichkeit,
wie ich zur Laufzeit an den generischen Typ einer Klasse komme?

Oder sollte ich das class-Object einfach im Konstruktor mitgeben??


----------



## Landei (8. Nov 2009)

Dragonfire hat gesagt.:


> Gibt es irgendeine Möglichkeit,
> wie ich zur Laufzeit an den generischen Typ einer Klasse komme?
> 
> Oder sollte ich das class-Object einfach im Konstruktor mitgeben??



Leider nur letzteres "dank" Type-Erasure. Aber schildere mal dein Problem, vielleicht lässt es sich auch anders lösen.


----------



## Spacerat (8. Nov 2009)

Tja... bei mir stellt sich gerade auch ein Problem, Generics betreffend ein.
Ich habe festgestellt, dass Collections verschiedener generischer Typen (z.B. ArrayList<Panel> und ArrayList<Window>) [c]equal()[/c] sind, solange sie Leer sind. Es muss doch auch ohne dass ich eine Klasse an den Konstruktor übergebe, eine Möglichkeit geben, zu Erkennen, dass diese Collections niemals [c]equal()[/c] werden können.


----------



## Dragonfire (8. Nov 2009)

Ich habe drei Arten von Panels die von einem generischen Superpanel erben.
Jetzt musste ich nur irgendwie die Informationen / Daten auf die Panels bekommen,
dafür habe ich der generische Superklasse eine Art Proxyklasse mitgegeben,
welcher je nach Typ des Panels den Inhalt zurückgibt.
Zurzeit muss in im jeweiligen Panel immer dem Proxy mit passenden Typ übergeben,
dabei ist der Aufruf immer derselbe.

Kann auch sein,
dass ich irgendwo ein Entwurfsfehler habe ...


----------



## tfa (9. Nov 2009)

Spacerat hat gesagt.:


> Tja... bei mir stellt sich gerade auch ein Problem, Generics betreffend ein.
> Ich habe festgestellt, dass Collections verschiedener generischer Typen (z.B. ArrayList<Panel> und ArrayList<Window>) [c]equal()[/c] sind, solange sie Leer sind. Es muss doch auch ohne dass ich eine Klasse an den Konstruktor übergebe, eine Möglichkeit geben, zu Erkennen, dass diese Collections niemals [c]equal()[/c] werden können.



Wenn sie leer sind, sind sie gleich. Siehe API-Doku. _Keine Äpfel_ ist genau das gleiche wie _keine Birnen_.
Davon abgesehen hat die Laufzeitumgebung keine Möglichkeit, den Typparameter herauszufinden. Das kann aber der Compiler. Wenn der das kann, kannst du es auch, d.h. der Vergleich zwischen typinkompatiblen Listen kann man sich sparen.


----------



## Landei (9. Nov 2009)

Spacerat hat gesagt.:


> Tja... bei mir stellt sich gerade auch ein Problem, Generics betreffend ein.
> Ich habe festgestellt, dass Collections verschiedener generischer Typen (z.B. ArrayList<Panel> und ArrayList<Window>) [c]equal()[/c] sind, solange sie Leer sind. Es muss doch auch ohne dass ich eine Klasse an den Konstruktor übergebe, eine Möglichkeit geben, zu Erkennen, dass diese Collections niemals [c]equal()[/c] werden können.



Du könntest Unterklassen machen:


```
public class WindowList extends ArrayList<Window> {

   @Override equals(Object o) {
       return (! (o instanceof  WindowList)) ? false : super.equals(o);
   }

}
```


----------



## Landei (9. Nov 2009)

Dragonfire hat gesagt.:


> Ich habe drei Arten von Panels die von einem generischen Superpanel erben.
> Jetzt musste ich nur irgendwie die Informationen / Daten auf die Panels bekommen,
> dafür habe ich der generische Superklasse eine Art Proxyklasse mitgegeben,
> welcher je nach Typ des Panels den Inhalt zurückgibt.
> ...



Möglich... dazu müsste man aber wenigstens mal ein paar Codefetzen sehen.


----------



## Spacerat (9. Nov 2009)

Landei hat gesagt.:


> Du könntest Unterklassen machen:
> 
> 
> ```
> ...


Ja, das ist stand der Dinge. Will sagen so musste ich es lösen.
Aber: Keine Äpfel sind keine Birnen...? Hmm...
Also... Ich wollte eine allgemeine typisierbare Collection schreiben, der man [c]ModifyEventListener[/c] hinzufügen kann und einer [c]ModifyEventQueue[/c] eine [c]Map<Modifyable, ModifyEventListener>[/c] ([c]Modifyable[/] ist dabei ein Interface, dass von der Collection implementiert wird.) anlegen, wo die erstellten Collections gesammelt werden. Pustekuchen: Egal, welche [c]Map[/c] ich verwende, ich kann dort nur eine leere Collection einfügen, auch wenn sie nur durch den Typparameter unterschieden werden. Was soll's. Letztendlich musste eine [c]Map[/c] geschaffen werden, die beim Einfügen, Entfernen und Lesen auf Identität statt auf inhaltliche Gleichheit prüft.


----------



## Dragonfire (9. Nov 2009)

Landei hat gesagt.:


> Möglich... dazu müsste man aber wenigstens mal ein paar Codefetzen sehen.



Ich hab jetzt natürlich viele Sachen ausgeschnitten,
für Vererbung habe ich mich entschieden,
weil die "Untertabellen" (ArticleTable, CustomerTable, ...) 
noch individuell angepasst werden müssen ....
Vielleicht hat aber wer eine bessere Lösung.
Würde mich mal interessieren.


```
public abstract class DvsTable<DATA> extends JPanel {
	protected SQLDataProxy<DATA> proxy;
	protected ArrayList<DATA> items;
	protected JTable table;
	protected AbstractTableModel tableModel;
	protected DvsDetailPanel<DATA> detailPanel;
	...

	public DvsTable(DatenSchnittstelle db, ...) {
		...
		init();
	}

	protected void init() {
		...
		initTable();
		initCustom();
	}
	
	abstract protected void initTable();
	
	abstract protected void initCustom();
}

public class CustomerTable extends DvsTable<Kunde> {

	public CustomerTable(...) {
		super(...);
	}

	@Override
	protected void init() {
		this.proxy = new SQLDataProxy<Kunde>(Kunde.class, this.db);
		super.init();
	}
	
	@Override
	protected void initCustom() {
		super.detailPanel = new CustomerDetailPanel(super.proxy, super.iM,
				super.info);
		((CustomerDetailPanel) super.detailPanel)
				.setExpandListener(new ActionListener() {

					@Override
					public void actionPerformed(ActionEvent e) {
						...
					}
				});
	}

	@Override
	protected void initTable() {
		super.tableModel = new CustomerTableModel();
	}

	private class CustomerTableModel extends AbstractTableModel {
		...
		@SuppressWarnings("boxing")
		@Override
		public Object getValueAt(int rowIndex, int columnIndex) {
			Object o = null;
			switch (columnIndex) {
			case 0:
				o = CustomerTable.super.items.get(rowIndex).getKundenID();
				break;
			case 1:
				o = CustomerTable.super.items.get(rowIndex).getName();
				break;
			case 2:
				o = CustomerTable.super.items.get(rowIndex).getVorname();
				break;

			default:
				break;
			}
			return o;
		}
	}
	
	public static void main(String[] args) {
		JFrame f = new JFrame("CustomerTable");
		...
		f.add(new CustomerTable(new DatenSchnittstelle(...), ...);
		...
	}
}

public abstract class DvsDetailPanel<DATA> extends JPanel {
	protected SQLDataProxy<DATA> proxy;
	protected DATA data;
	...

	public DvsDetailPanel(...) {
		...
		init();
	}

	protected void init() {
		...
	}
}

public class CustomerDetailPanel extends DvsDetailPanel<Kunde> {
	private JTextField kundenTextField;
	...

	public CustomerDetailPanel(...) {
		super(...);
	}

	@Override
	protected void init() {
		this.kundenTextField = new JTextField(super.data.getKundenName());
		JPanel.add(this.kundenTextField);
		...
		super.init();
	}
}

public class SQLDataProxy<E> {
	private Class<?> e;
	private DatenSchnittstelle db;
	...
	
	public SQLDataProxy(Class<?> e, DatenSchnittstelle db) {
		this.e = e;
		this.db = db;
	}

	...

	@SuppressWarnings("unchecked")
	public E getItem(int id) {
		E item = null;
		if (this.e == Artikel.class) {
			item = (E) this.db.getArtikel(id);
		} else if (this.e == Kunde.class) {
			item = (E) this.db.getKunde(id);
		}
		return item;
	}

	public void addItem(E item) {
		if (this.e == Artikel.class) {
			this.db.addArtikel((Artikel) item);
		} else if (this.e == Kunde.class) {
			this.db.addKunde((Kunde) item);
		}
	}
}

public class DatenSchnittstelle {
	...
	public Artikel getArtikel(int ArtikelNr) {
		...
	}
	
	public Kunde getKunde(int kundenID) {
		...
	}
}
```

Bei table_gui.pdf ist das Ganze ein CustomTable,
das Panel im unteren des JSplitPane ist ein CustomerDetailPanel (erbt von DetailPanel)...

Bei Fragen einfach melden


----------



## Landei (9. Nov 2009)

Spacerat hat gesagt.:


> Ja, das ist stand der Dinge. Will sagen so musste ich es lösen.



Oder halt über Class-Objekte, dann brauchst du nur eine Extra-Klasse:

```
RuntimeTypeList<T> extends ArrayList<T> {

   private final Class<T> clazz;

   public RuntimeTypeList(Class<T> clazz) {
       this.clazz = clazz;
   }
   @Override equals(Object o) {
       return (! (o instanceof  RuntimeTypeList) || 
               ! clazz.equals(((RuntimeTypeList)o).clazz)) ? false : super.equals(o);
   }
 
}
```


----------

