51

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.

dreadwail
  • 15,098
  • 21
  • 65
  • 96
  • 15
    The desire to do this is almost guaranteed to be a bad design choice. Chances are that the if statement requires knowledge of each "type" in order to be implemented, so you should probably have all those classes be of the same parent type, and "do this" should be a virtual method. It may involve wrapping classes, but overall your design will improve if you go down this road. – Bill K Jun 16 '09 at 21:29
  • 2
    The possibilities in the if statements are edge cases among all possible calling types, which require entirely separate implementations altogether. I'm in agreement with what you're saying here, but the specific application is a rock and a hard place. – dreadwail Jun 16 '09 at 21:32
  • 1
    The need to get the class for a generic type param is not always a bad design decision. One legitimate requirement (mine! :) could be a method call that requires a Class<> parameter. – dahvyd Aug 25 '12 at 11:06
  • I need to do this as well for a generic DTO convertor. IMO this is a very legitimate question. – Dave Aug 05 '13 at 14:27

8 Answers8

60

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];
}
sgriffinusa
  • 4,203
  • 1
  • 25
  • 26
jnt30
  • 1,367
  • 2
  • 15
  • 21
  • +1 And this is pretty much the solution I went with after further research, so I've changed it to my accepted answer. – dreadwail Jun 19 '09 at 03:49
  • 13
    Gives me `Exception in thread "main" java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType` Are you sure this works? – Ram May 12 '11 at 10:28
  • 11
    Here is what happens in Android: getClass().getGenericSuperclass() returns a Class object representing java.lang.Object, which does not cast to ParameterizedType. If your class inherits from a dummy Base, the return value represents a "Base" and getActualTypeArguments()[0] returns is a Type with name "T", which does not even cast to Class. No go. – misiu_mp Aug 13 '11 at 12:18
  • 4
    It's worth noting here that this will only work for classes whose type parameters are explicitly declared in java code. This type of logic will not work for things like mocks implemented via dynamic proxying. – Paul Morie May 09 '12 at 17:47
  • How do I then check if what type this returned class is? eg `if (returnedClass isa Double).... if (returnedClass isa Integer)...` – dwjohnston Nov 08 '12 at 23:18
  • Java has a few options, the most direct to what you asked is the instanceof such as if (returnedClass instanceof Double). See [This Question](http://stackoverflow.com/questions/496928/what-is-the-difference-between-instanceof-and-class-isassignablefrom) for more information – jnt30 Nov 25 '12 at 03:07
  • Does it mean that info about generics of a class is stored with class information in permgen? –  Jun 24 '13 at 20:32
  • Cannot understand why this is an answer. I always got exception and other people too. – 5YrsLaterDBA Dec 20 '14 at 20:17
  • What version of Java are you experiencing an issue with? I haven't used this with Java 8 yet, and I know this causes problems with Android. – jnt30 Dec 22 '14 at 17:28
  • Anyone have any clues how to do a similar thing for _static function_ type parameters? – Nyerguds Feb 05 '16 at 09:05
  • I have the same problem but need it to work on Primitives. Any idea? – Ross Brigoli Jan 16 '17 at 11:41
  • This works if you have a generic abstract class and the subclass is declared like `class SubClass extends SuperClass` - so `Type` is defined at compile time. – Mindwin Remember Monica Apr 27 '17 at 17:44
  • Link is dead, [archive link](https://web.archive.org/web/20120228160754/http://blog.xebia.com/2009/02/07/acessing-generic-types-at-runtime-in-java/) – Code Eyez Nov 08 '17 at 20:54
44

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.

Daniel Schneller
  • 13,728
  • 5
  • 43
  • 72
13

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.

Michael Myers
  • 188,989
  • 46
  • 291
  • 292
  • This seems like the best solution, albeit less pretty than a true blue generic class. Thanks for the input. – dreadwail Jun 16 '09 at 21:35
  • Again, I urge you to strongly consider polymorphism rather than an if-statement based on type. – Michael Myers Jun 16 '09 at 21:36
  • Unfortunately the items in consideration are not relating in any way, so a polymorphic solution isn't practical. The possible items that would fall into the if-statement logic are edge cases of all other 'normal' processing that must be treated entirely differently altogether, in no relational way. – dreadwail Jun 16 '09 at 21:39
  • 1
    @dxmio: The items relate in this way: that you're creating a Foo<> of them. I'm not saying that to be flip: you could create a "Fooable" interface and have each of them implement that - and the interface could have a single method that returned their type (or, better, the enumeration). – Carl Manaster Jun 16 '09 at 21:49
  • This fails with Java <= 1.6.0.32 with an incomparable types error. – Dave Aug 08 '13 at 03:07
  • can you give a concrete example of what you mean? The issue I'm having is that I cannot instantiate a new class of the same type within a method I pass it to... – Vincent Gerris Dec 24 '18 at 12:50
3

The Problem is that most of the Generic stuff will disappear during compilation.

One common solution is to save the type during the creation of the Object.

For a short introduction in the Type Erasure behaviour of java read this page

Janusz
  • 187,060
  • 113
  • 301
  • 369
1

If you know a few specific types that are meaningful, you should create subclasses of your generic type with the implementation.

So

public class Foo<T>

public ???? Bar()
{
    //else condition goes here
}

And then

public class DateFoo extends Foo<Date>

public ???? Bar()
{
    //Whatever you would have put in if(T == Date) would go here.
}
Ryan Shillington
  • 23,006
  • 14
  • 93
  • 108
0

The whole point of a generic class is that you dont need to know the type that is being used....

pauljwilliams
  • 19,079
  • 3
  • 51
  • 79
  • 2
    True enough, however if I design a class that can be instantiated generically to be of any type, I want to handle a specific subset of all possible types as edge cases with specific logic. – dreadwail Jun 16 '09 at 21:29
  • Then create different subtypes. Or delegate to strategies (or similar). – Tom Hawtin - tackline Jun 16 '09 at 22:10
0

It looks like what you want is in fact not a Generic class, but an interface with a number of different implementations. But maybe it would become clearer if you stated your actual, concrete goal.

Michael Borgwardt
  • 342,105
  • 78
  • 482
  • 720
0

I agree with Visage. Generics is for compile-time validation, not runtime dynamic typing. Sounds like what you need is really just the factory pattern. But if your "do this" isn't instantiation, then a simple Enum will probably work just as well. Like what Michael said, if you have a slightly more concrete example, you'll get better answers.

aberrant80
  • 12,815
  • 8
  • 45
  • 68
  • I have to disagree here, and there have been several mentions of a non-polymorphic solution being necessary in the specific problem space, and I've been quite happy with the answers I've received. – dreadwail Jun 19 '09 at 03:47