Skip to main content

Identifying Generics

11 replies [Last post]
aviadbd
Offline
Joined: 2004-07-04
Points: 0

Anyone thought of adding the ability to discover which generics types are currently effective on a certain class?

Meaning: I have a field defined as:

Collection myCollection;

Reflecting it's type would reveal only the type parameters (using getTypeParameters) which would be named "E" with no bounds. There is no way to get "Integer" with the current reflection ability - Even though the JRE could keep that information when a generic class is instantiated.

Two options I can see on doing that:
* Saving the info for each instance. Adds some info to a class, indeed.
* Creating a different Class for each type parameters set (like what's being done with arrays of different compound types and dimensions?). Might slow down reflection even more, but that's what reflection's all about.. :)

Aviad.

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
aviadbd
Offline
Joined: 2004-07-04
Points: 0

Also, you need to remember that the data needs to be stored in the bytecode of the .class file itself. Currently, it doesn't seem like instances in inner scopes carry on their generic definitions.

aviadbd
Offline
Joined: 2004-07-04
Points: 0

Basically, it sounds like its about claiming that some things just won't work on earlier JVMs. Or, produce a minor version for each that knows to ignore this extra data in the .class files.

I don't know the exact complications of it. Anyone?

tackline
Offline
Joined: 2003-06-19
Points: 0

It's retrieving full type information from objects which is the problem (as it doesn't exist). Type information from fields and the like is straightforward. I put a little code together to dump a field's type.

import java.lang.reflect.*;

class DumpFieldType {
public static void main(String[] args) throws Throwable {
dumpFieldType(Example.class, "field");
}
private static void dumpFieldType(
Class clazz, String fieldName
) throws Throwable {
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
dumpType(field.getGenericType());
dump("\n");
}
private static void dumpType(Type type) {
if (type instanceof GenericArrayType) {
// Type[]
GenericArrayType t = (GenericArrayType)type;
dumpType(t.getGenericComponentType());
dump("[]");
} else if (type instanceof ParameterizedType) {
// TypeA.TypeB or Map.Entry
ParameterizedType t = (ParameterizedType)type;
Type owner = t.getOwnerType();
if (owner != null) {
dumpType(owner);
dump(".");
}

dumpType(t.getRawType());
Type[] actualTypes = t.getActualTypeArguments();
if (actualTypes != null) {
dump("<");
dumpTypes("", ", ", actualTypes);
dump(">");
}
} else if (type instanceof TypeVariable) {
// T extends X&Y
TypeVariable t = (TypeVariable)type;
dump(t.getName());
dumpUpperBounds(t.getBounds());
} else if (type instanceof WildcardType) {
// ? extends X&Y super Z
WildcardType t = (WildcardType)type;
dump("?");
dumpUpperBounds(t.getUpperBounds());
dumpTypes(" super " , "|", t.getLowerBounds());
} else if (type instanceof Class) {
// Type
Class t = (Class)type;
dump(t.getSimpleName());
} else if (type == null) {
// We probably messed up.
dump("?[? null ?]?");
} else {
// Eh?
dump("?[" + type.getClass() + "]?");
}
}
private static void dumpUpperBounds(Type[] types) {
if (types.length == 1 && types[0] == Object.class) {
return;
}
dumpTypes(" extends ", "&", types);
}
private static void dumpTypes(String prefix, String sep, Type[] types) {
String s = prefix;
for (Type type : types) {
dump(s);
dumpType(type);
s = sep;
}
}
private static void dump(String str) {
System.out.print(str);
}
}

import java.util.Collection;

public class Example {
private Collection field;
}

aviadbd
Offline
Joined: 2004-07-04
Points: 0

However, as you stated yourself, this is only accessible through methods and fields. What happens if I get a POJO?

And it should be much simpler than what you've shown. Seriously, we're not talking about a language for rocket scientists. This is something that the framework Could know about, just chooses not to! It should be simple to access it.

aviadbd
Offline
Joined: 2004-07-04
Points: 0

What about a parameter to the compiler? When -no-erasure is specified, it will Not perform erasure. The code will Not be compatible and runnable at JVMs lower than 1.4, and any method in Class which would normally return the parameter types' Real types would return an empty list (or a list of the highest bound, as left currently by erasure).

Would that make more sense?

alexlamsl
Offline
Joined: 2004-09-02
Points: 0

Generics is implemented through type-erasure, hence the information that you want (Integer in Collection ) would not be present in the class file.

Although I often find that such information can be rendered redundant by a change of design, when one needs to one can employ the following constructor pattern to preserve such information:

[pre]
public class A {

private final Class type;

// Option 1
public A(T obj) // and other params
{
type = (Class ) obj.getClass();
// ...
}

// Option 2
public A(Class type) // and other params
{
this.type = type;
// ...
}

}
[/pre]

aviadbd
Offline
Joined: 2004-07-04
Points: 0

Oh I know about Erasure. You can read my thoughts about it, which are similar to what I wrote above, here: http://javachaos.blogspot.com/2005/12/avoiding-erasure.html

What I'm saying is that Erasure and keeping the identity of the generic types aren't contradictive. If the JVM is of version 6, it can save the data in Object or generate a different Class instance. That's my suggestion - To maintain this data during the runtime.

soupdragon
Offline
Joined: 2006-01-07
Points: 0

It seems to me that, if you don't do erasure, you have to add the generics information to the Object memory structure. For small objects this could be a substantial increase in overhead, plus a significant change in the JVM. And there [i]are[/i] small generic objects (e.g. references).

tackline
Offline
Joined: 2003-06-19
Points: 0

Currently objects have a pointer to their runtime class information. If they had runtime generic parameter information, they would still only need a single pointer.

The necessary change is that instead of pointing to class information, a structure is referenced containing the old erased class pointer and pointers to the generic arguments. In most cases there would be a one-to-one correspondence between these type information structures and non-abstract classes, so the memory overhead is quite minimal.

Reference objects may be bigger than you expect. To be useful, they also need to point to something.

soupdragon
Offline
Joined: 2006-01-07
Points: 0

> Currently objects have a pointer to their runtime
> class information. If they had runtime generic
> parameter information, they would still only need a
> single pointer.
>

You could do it that way, of course, but wouldn't that signifcantly increase access times to class, which I presume the JVM needs to do rather a lot?

tackline
Offline
Joined: 2003-06-19
Points: 0

> You could do it that way, of course, but wouldn't
> that signifcantly increase access times to class,
> which I presume the JVM needs to do rather a lot?

Not significantly. One extra load. It's not like anyone cares too much about "virtual" method call overhead as it is. I'd be surprised if reduction in runtime type checking didn't more than make up for the penalty.