Skip to main content

Determine generics type alternative

4 replies [Last post]
prodoc
Offline
Joined: 2009-05-27

Hi,

I though I had a clever way of using java generics but this turned out not to be the case.

In the following class I'm using the class parameter type to create a List with items of the same type. This all works nice and well, except for the actual query bit. The table it should get its data from is now hard-coded ("Threat"). This should, however, be based on the parameter type. My plan was to determine the type and use that to create the query. Determining the type turns out not to be possible at all, with something like instanceof.

Now I'm looking for a clean alternative but I can't think of one. Do you have any idea's or suggestions?

------------------------------------------------------
public class AddDeviceTool implements DeviceTool, EventListener {
private DeviceManager deviceManager;
private EntityManager entityManager;
private Query deviceQuery;
private List deviceList;

public AddDeviceTool(DeviceManager deviceManager) {
this.svgCanvas = deviceManager.getCanvas();
this.deviceManager = deviceManager;

entityManager = java.beans.Beans.isDesignTime() ? null : javax.persistence.Persistence.createEntityManagerFactory("MPSFPU").createEntityManager();
deviceQuery = java.beans.Beans.isDesignTime() ? null : entityManager.createQuery("SELECT d FROM " + "Threat" + " D");

@SuppressWarnings("unchecked")
List list = (List) (java.beans.Beans.isDesignTime() ? java.util.Collections.emptyList() : deviceQuery.getResultList()); deviceList = list;
}

public void handleEvent(final Event evt) {
...
deviceManager.addDevice(device, mousePosition);
...
}

....
}
------------------------------------------------------

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
tarbo
Offline
Joined: 2006-12-18

Because generics are erased form the compiled code, any runtime reference you want has to be supplied explicitly. This is the reason why, for instance, classes like EnumMap and EnumSet require their Enum class explicitly specified.

[code]
public class AddDeviceTool implements DeviceTool, EventListener {
private DeviceManager deviceManager;
private EntityManager entityManager;
private Query deviceQuery;
private List deviceList;
private Class deviceType; // Store your supplied type

public AddDeviceTool(DeviceManager deviceManager /*supply your type*/, Class deviceType) {
this.svgCanvas = deviceManager.getCanvas();
this.deviceManager = deviceManager;
this.deviceType = deviceType;

entityManager = java.beans.Beans.isDesignTime() ? null : javax.persistence.Persistence.createEntityManagerFactory("MPSFPU").createEntityManager();
deviceQuery = java.beans.Beans.isDesignTime() ? null : entityManager.createQuery("SELECT d FROM " + /* map the class to your table somehow */ + " D");

@SuppressWarnings("unchecked")
List list = (List) (java.beans.Beans.isDesignTime() ? java.util.Collections.emptyList() : deviceQuery.getResultList()); deviceList = list;
}
[/code]

Hope this solves your problem,
Jonathan

prodoc
Offline
Joined: 2009-05-27

Thank you for your replies.

Unfortunately I don't understand how ljnelson's suggestion about super type tokens would help me. Simply because I just don't get the whole super type tokens concept :-$

If I understand tarbo's suggestion correctly his intend is to supply an instance of the desired device class as a parameter when creating an instance of AddDeviceTool. It is, however, not untill the list has been gotten from the db that an actual instance exists. By going in this direction I might as well supply a simple string parameter which get's inserted into the query string:

new AddDeviceTool(deviceManager, "Threat");

It is this duplication that I wanted to prevent. Unless, of course, I'm overlooking something ;-)

tarbo
Offline
Joined: 2006-12-18

You needn't pass an actual instance, but yes, you need [i]some[/i] runtime indication of what table you need to load from, and generics can't help you there. Either you explicitly code in the mapping to table names (not recommended), or you supply some dynamic way to retrieve this.

In an admittedly dated project, we had a static final field in every database-related class that contained the table name it was stored in. This worked in the following way:

[code]abstract class DatabaseObject { /* ... */ }
class Birdie extends DatabaseObject {
public static final String tablename = "birdies";
}

E newInstance(String uid, Class type) {
String tablename = type.getField("tablename").get(null);
/* and so on */
}[/code]

This is [i]a[/i] way to do it --a little crude-- but it works for simple purposes. You could also wrap this in a kind of Provider class that can map your types to a table name or query or whatever is most convenient to you.

The super type token concept --if I understand correctly-- is a little bit complicated, but basically boils down to this. Generics are erased at runtime. Generified classes can be extended and "lose" some dimension of genericity:

[code]interface Collection { /*... */ }
interface StringCollection extends Collection {}[/code]

You can't ask a Collection what its type parameter is because this knowledge is erased at runtime. But StringCollection knows that it is a Collection, and thus knows it inherits from Collection with a type parameter of String.

It is a trick you can use, but I've yet to see it used effectively. Perhaps I haven't looked closely enough for examples or proper uses.

Hope this helps,
Jonathan

ljnelson
Offline
Joined: 2003-08-04

Would super type tokens (http://gafter.blogspot.com/2006/12/super-type-tokens.html) help you out here?

Create an inner class super type token for the T type? Then you could rummage around in it and come up with the class name (I haven't tried this).

Best (and good luck),
Laird