How to create a class literal of a known type: Class<List<String>>
Asked Answered
M

9

72

Take the following:

public Class<List<String>> getObjectType() {
    // what can I return here?
}

What class literal expression can I return from this method which will satisfy the generics and compile? List.class won't compile, and neither will List.<String>class.

If you're wondering "why", I'm writing an implementation of Spring's FactoryBean<List<String>>, which requires me to implement Class<List<String>> getObjectType(). However, this is not a Spring question.

edit: My plaintive cries have been heard by the powers that be at SpringSource, and so Spring 3.0.1 will have the return type of getObjectType() changed to Class<?>, which neatly avoids the problem.

Mercurio answered 6/1, 2010 at 10:45 Comment(0)
P
62

You can always cast to what you need, like

return (Class<List<String>>) new ArrayList<String>().getClass();

or

return (Class<List<String>>) Collections.<String>emptyList().getClass();

But I assume that's not what you are after. Well, it works, with a warning, but it isn't exactly "beautiful".

I just found this:

Why is there no class literal for wildcard parameterized types?

Because a wildcard parameterized type has no exact runtime type representation.

So casting might be the only way to go.

Pissed answered 6/1, 2010 at 11:13 Comment(1)
Your first example did not work for me (SE 1.6), only the second example did. Instantiating the ArrayList to a variable and returning myVar.getClass() works though. found : java.lang.Class<capture#972 of ? extends java.util.ArrayList> required: java.lang.Class<java.util.List<com.foo.Bar>>Asteriated
R
17

You should never use the construct Class<List<String>>. It is nonsensical, and should produce a warning in Java (but doesn't). Class instances always represent raw types, so you can have Class<List>; that's it. If you want something to represent a reified generic type like List<String>, you need a "super type token" like Guice uses:

http://google-guice.googlecode.com/git/javadoc/com/google/inject/TypeLiteral.html

Rna answered 6/1, 2010 at 18:30 Comment(1)
I agree that it's not a good idea, but Java permits it, and if you're trying to implement an interface, you may have no choice in the matter, so we still need a way of doing it (semi-)elegantly.Mercurio
S
14

You can implement that method like this:

public Class<List<String>> getObjectType() {
    return (Class<List<String>>) ((Class)List.class);
}
Stagy answered 13/4, 2014 at 16:15 Comment(2)
I like this way because you don't need create a new instances.Grefer
The outer cast is unnecessary. You can write it like this instead: return (Class)List.class;, which I find easier to read.Tirpitz
M
9

The existence of a Class<List<String>> is inherently dangerous. here's why:

// This statement generates a warning - for a reason...
Class<List<String>> unsafeListClass = (Class<List<String>>) (Class<?>) List.class;

List<Integer> integerList = new ArrayList<Integer>(); // Ok
integerList.add(42); // Ok

System.out.println(unsafeListClass.isInstance(integerList)); // Prints "true".
List<String> stringList =
   unsafeListClass.cast(integerList); // Succeeds, with no warning!
stringList.add("Hello, World!"); // Also succeeds with no warning

for (int x: integerList) {
    // Compiles without warning, but throws ClassCastException at runtime
    System.out.println(100-x);
}
Mythomania answered 12/2, 2010 at 0:12 Comment(1)
That is a strange example. Since generics are not reified in runtime runtime casting bypasses the generic type system in many cases. Generics are mostly a compile time construct that allows you to further constrain a type from a class type. It would be useful if you could keep the generic signature because, as long as you don't cast, would make a lot of code more readable AND typesafe.Reade
H
7

Found this link on springframework.org which gives some insight.

E.g.

List<String> myList = new ArrayList<String>();
return (Class<List<String>>)myList.getClass();
Howlyn answered 6/1, 2010 at 11:6 Comment(2)
@Bozho: the springframework link has a different method signature java.lang.Class<? extends T> getObjectType(). I've updated my answer to reflect the signature of the OP's question.Howlyn
Link in answer is dead - "issues.springframework.org | DNS_PROBE_FINISHED_NXDOMAIN".Marlie
S
2

Check out this discussion on the SUN forums:

http://forums.sun.com/thread.jspa?threadID=5253007

And the referenced blog post that describes a work around by using "super type tokens":

http://gafter.blogspot.com/2006/12/super-type-tokens.html

Sputnik answered 6/1, 2010 at 11:21 Comment(1)
First link in answer is outdated.Marlie
N
1

I'm not sure if this is possible at all, since any class literal will be compiled to Class.forName(...) and since this happens at runtime there is no generic information left.

Nigrescent answered 6/1, 2010 at 11:8 Comment(0)
J
0

What about this:

public class TestMain {
    public static void main(String[] args) throws Exception {
        Type type = TestMain.class.getMethod("dummy").getGenericReturnType();
        System.out.println("type = " + type);
    }

    public List<Integer> dummy() {return null;}
}

This prints:

type = java.util.List<java.lang.Integer>
Jellied answered 9/5, 2013 at 9:11 Comment(0)
A
0

The following approach is problematic:

> public Class<List<String>> getModelType() {
>   return (Class<List<String>>) new ArrayList<String>().getClass();
> }

e.g. if you want to test whether an object say of type

org.eclipse.emf.common.util.BasicEList<String> 

is of type

List<String> 

based on the result of the aforementioned getModelType() approach, for example:

BasicEList<String> fromObject = ...;
if (getModelType().isAssignableFrom(fromObject.getClass())) {
    transferFromModelToUi(getModelType().cast(fromObject));
}

it will result in false whereas it should be true because both objects implement the interface List (since getModelType() returns a Class object of type List and not ArrayList).

Here is an approach that worked for me (a bit cumbersome but leads to correct results in the example above, could be moved to a static initializer):

public Class<List<String>> getModelType() {
    Class<?> arrayListClass = new ArrayList<String>().getClass();
    Class<?>[] interfaces = arrayListClass.getInterfaces();
    int index = 0;
    for (int i = 0; i < interfaces.length; i++) {
        if (interfaces[i].equals(List.class)) {
            index = i;
            break;
        }
    }
    return (Class<List<String>>) interfaces[index];
}
Adiathermancy answered 28/3, 2014 at 7:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.