Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java Generic Class - Determine Type

Tags:

java

generics

If I am creating a java class to be generic, such as:

public class Foo<T>

How can one determine internally to that class, what 'T' ended up being?

public ???? Bar()
{
    //if its type 1
    //    do this
    //if its type 2
    //    do this
    //if its type 3
    //    do this
    //if its type 4
    //    do this
}

I've poked around the Java API and played with the Reflection stuff, instanceof, getClass, .class, etc, but I can't seem to make heads or tails of them. I feel like I'm close and just need to combine a number of calls, but keep coming up short.

To be more specific, I am attempting to determine whether the class was instantiated with one of 3 possible types.

like image 663
dreadwail Avatar asked Jun 16 '09 21:06

dreadwail


People also ask

How do you indicate that a class has a generic type parameter?

A generic type is declared by specifying a type parameter in an angle brackets after a type name, e.g. TypeName<T> where T is a type parameter.

What is the generic class type called?

The type parameter section of a generic class can have one or more type parameters separated by commas. These classes are known as parameterized classes or parameterized types because they accept one or more parameters.

What is generic data type in Java?

Generics means parameterized types. The idea is to allow type (Integer, String, … etc., and user-defined types) to be a parameter to methods, classes, and interfaces. Using Generics, it is possible to create classes that work with different data types.


3 Answers

I've used a similar solution to what he explains here for a few projects and found it pretty useful.

http://blog.xebia.com/2009/02/07/acessing-generic-types-at-runtime-in-java/

The jist of it is using the following:

 public Class returnedClass() {
     ParameterizedType parameterizedType = (ParameterizedType)getClass()
                                                 .getGenericSuperclass();
     return (Class) parameterizedType.getActualTypeArguments()[0];
}
like image 154
jnt30 Avatar answered Oct 17 '22 05:10

jnt30


In contrast to .NET Java generics are implemented by a technique called "type erasure".

What this means is that the compiler will use the type information when generating the class files, but not transfer this information to the byte code. If you look at the compiled classes with javap or similar tools, you will find that a List<String> is a simple List (of Object) in the class file, just as it was in pre-Java-5 code.

Code accessing the generic List will be "rewritten" by the compiler to include the casts you would have to write yourself in earlier versions. In effect the following two code fragments are identical from a byte code perspective once the compiler is done with them:

Java 5:

List<String> stringList = new ArrayList<String>();
stringList.add("Hello World");
String hw = stringList.get(0);

Java 1.4 and before:

List stringList = new ArrayList();
stringList.add("Hello World");
String hw = (String)stringList.get(0);

When reading values from a generic class in Java 5 the necessary cast to the declared type parameter is automatically inserted. When inserting, the compiler will check the value you try to put in and abort with an error if it is not a String.

The whole thing was done to keep old libraries and new generified code interoperable without any need to recompile the existing libs. This is a major advantage over the .NET way where generic classes and non-generic ones live side-by-side but cannot be interchanged freely.

Both approaches have their pros and cons, but that's the way it is in Java.

To get back to your original question: You will not be able to get at the type information at runtime, because it simply is not there anymore, once the compiler has done its job. This is surely limiting in some ways and there are some cranky ways around it which are usually based on storing a class-instance somewhere, but this is not a standard feature.

like image 44
Daniel Schneller Avatar answered Oct 17 '22 05:10

Daniel Schneller


Because of type erasure, there is no way to do this directly. What you could do, though, is pass a Class<T> into the constructor and hold onto it inside your class. Then you can check it against the three possible Class types that you allow.

However, if there are only three possible types, you might want to consider refactoring into an enum instead.

like image 13
Michael Myers Avatar answered Oct 17 '22 07:10

Michael Myers