I have a class with a generic type.
import java.lang.reflect.Array;
public abstract class MyClass<T> {
private Class<T> clazz;
private Class<?> arrayClazz;
public MyClass() {
clazz = ClassUtils.getParameterizedClass(getClass());
arrayClazz = Array.newInstance(clazz, 0).getClass();
}
}
The Utils method reads the generic type of the given class so I don't have to pass the same class as a constructor parameter. What I'm trying to achieve is getting the array class of clazz.
For example if T is String then
clazz = String.class;
arrayClazz = String[].class;
I currently solved it by creating a new instance of T[] and reading its class. I wanted to know if there's a better way or if there are any downsides with this method.
Update
What I'm trying to do: I have a generic DataProvider which requests JSON from a server. I use GSON to parse the response.
public abstract class DataProvider<T> {
private final Class<T> resourceClass;
private final Class arrayClass;
protected DataProvider() {
this.resourceRootPath = resourceRootPath;
this.resourceClass = ClassUtils.getParameterizedClass(getClass());
arrayClass = Array.newInstance(resourceClass, 0).getClass();
}
public void get(String id) {
...
T obj = gson.fromJson(response.body().charStream(), resourceClass)
...
}
public void list(String id) {
...
T[] objs = gson.fromJson(response.body().charStream(), arrayClass)
...
}
}
You should look up Type Erasure, which may prevent a clean way of doing what you're asking for. (Generics can't help you here.)
The problem is that in Java, generic type data is used at compile time to ensure everything looks good... and then Java forgets about it entirely. The types aren't compiled in in the way you'd hope, they're just gone.
Because of that, your approach is (I think!) the cleanest, even if it feels hacky. You need to instantiate one and use reflection to get it's type.
Related
I come from a C# background and am having trouble with Java generics. I am trying to call a generic method that requires an instance of Class<T> as a param. Normally this is a matter of passing SimpleType.class. In this case though, the type I need is not a simple type, but has its own type parameter that needs to be included in the Class instance (i.e. I need a Class<OuterType<InnerType>>). How do I get this?
Note: To make things a bit more complicated, this is taking place within another generic method. So while the "outer" type is known at compile type, the inner is known only through its own Class instance. See the code:
public <T> T deserialize(InputStream stream, Class<T> clazz) {
com.fasterxml.jackson.databind.ObjectMapper mapper = getJsonMapper();
//Need an instance of Class<MessageWrapper<T>>
MessageWrapper<T> wrapper = mapper.readValue(stream, MessageWrapper.class);
return wrapper.message;
}
private class MessageWrapper<T> {
public T message;
}
What you can try is this:
public <T> T deserialize(InputStream stream, Class<T> clazz) {
com.fasterxml.jackson.databind.ObjectMapper mapper = getJsonMapper();
//Need an instance of Class<MessageWrapper<T>>
MessageWrapper<T> wrapper =mapper.readValue(jsonString, new TypeReference<MessageWrapper<T>>() {});
return wrapper.message;
}
As well, the class should be or static, or public within its own file:
public class MessageWrapper<T> {
public T message;
}
Or:
private static class MessageWrapper<T> {
public T message;
}
I'm trying to get the solution to achieve the following:
public final static <T> Class<Set<T>> getSetClass(Class<T> cls) {
...
}
Where cls is for example String.class and the method then returns the Class<Set<String>> object. But of course, as cls is variable, it could also get Boolean.class and then return a Class<Set<Boolean>> object.
How can I achieve this?
This works for me:
#SuppressWarnings("unchecked")
public <T> Class<Set<T>> getSetClass(Class<T> cls) {
Set<T> set = new HashSet<>();
return (Class<Set<T>>) set.getClass();
}
Well, it compiles; how useful it is to solve your "real" problem; I can't tell yet. And obviously; that cls parameters goes unused here.
But it does something:
#Test
public void test() {
Class<Set<Integer>> setClass = new PartitionPropertiesTest().getSetClass(Integer.class);
System.out.println("whatever: " + setClass);
}
prints:
whatever: class java.util.HashSet
There is no Class<Set<String>> object.
Generic types are erased at runtime, so there is only an instance of Class<Set> (namely Set<?>.class) shared by all the Class<Set<?>>. There is no way to get back to String from this object.
For this very reason, your JSON library has a TypeRef class to capture the generic type information. You just use that instead of Class.
You mean, how to define that the return type depends on the parameter type?
Try this:
public final static <T> Class<Set<T>> getSetClass(Class<T> cls) {
...
}
I want to use the class information that was captured by the setup of a generic method in Java to potentially create an instance. But I can't see how to reference the class without an actual instance. Is it possible?
public class Fancy {
static public <TypeToFind> TypeToFind createInSomeCase() {
// any type of logic with TypeToFind "class" generic will do, I just want to reference it.
// the below code is invalid, I could also declare a variable, but can't always create an instance to get the class
if (TypeToFind.getClass().equals(Something.class)) {
return TypeToFind.getInstance();
}
}
}
... so later on I could do:
TheResultIsTheParameter t = Fancy.createInSomeCase();
... instead of
TheResultIsAParameter t = Fancy.createInSomeCase(TheResultIsAParameter.class);
... or
TheResultIsAParameter t = Fancy.createInSomeCase(t);
Am I making this too complicated?
You can't do it, because generics are lost at runtime (due to type erasure). You have to pass a Class<?> parameter
Well, you require somethink that is logical, unfortunattelly generics in Java are only a syntactic sugar for reflection.
List<MyClass> list;
(...)
MyClass my = list.get(0);
will compile to
MyClass my = (MyClass) list.get(0);
and this is what will you see in bytecode.
What is more, using reflection or casting to untyped list you can put any object into list and in both codes you'll get ClassCastException.
So the generics exists only on compiler level. A big feature which adds nothing new, only shortens a code in most cases.
As long as you do not try and statically (at compile time) reference any particular class, nothing prevents you from doing something like this:
public class GenericsTest {
#Test
public void testMe() {
GenericsTest test = new GenericsTest();
System.out.println(test.get("Hello").getClass());
}
public GenericsTest() {
super();
}
public <T extends Object> T get(T entity) {
return newInstanceForClass((Class<T>)entity.getClass());
}
public <T extends Object> T newInstanceForClass(Class<T> clazz) {
try {
return clazz.newInstance();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
But as you can see, you need to pass in an object of the class you are trying to instantiate, which might not be want you are after. In which case the only other option is to pass in a Class parameterized with the generic type, for reasons that other posters have eloquently stated.
How does one get a parameterized Class object to be used as a method argument?
class A<T>
{
public A(Class<T> c)
{
}
void main()
{
A<String> a1 = new A<String>(String.class); // OK
A<List<String>> a2 = new A<List<String>>(List<String>.class); // error
A<List<String>> a3 = new A<List<String>>(Class<List<String>>); // error
}
}
Why do I want to do that, you may ask? I have a parameterized class whose type is another parameterized class, and whose constructor requires that other class type as an argument. I understand that runtime classes have no information on their type parameters, but that shouldn't prevent me from doing this at compile time. It seems that I should be able to specify a type such as List<String>.class. Is there another syntax to do this?
Here is my real usage case:
public class Bunch<B>
{
Class<B> type;
public Bunch(Class<B> type)
{
this.type = type;
}
public static class MyBunch<M> extends Bunch<List<M>>
{
Class<M> individualType;
// This constructor has redundant information.
public MyBunch(Class<M> individualType, Class<List<M>> listType)
{
super(listType);
this.individualType = individualType;
}
// I would prefer this constructor.
public MyBunch(Class<M> individualType)
{
super( /* What do I put here? */ );
this.individualType = individualType;
}
}
}
Is this possible?
How about just cast?
super((Class<List<M>>)List.class);
Class literals are not going to have the type parameters that you want.
Remember you will NOT get a List as a class in runtime, and the right approach would probably be using TypeToken as BalusC told you. Without TypeToken, you can't cast to List, but you can create something like this:
public static class MyBunch2<List_M extends List<M>, M> extends Bunch<List_M>
{
Class<M> individualType;
#SuppressWarnings("unchecked")
public MyBunch2(Class<M> individualType)
{
super((Class<List_M>) List.class);
this.individualType = individualType;
}
}
Since List_M extends List<M> this is not as typesafe as you may wish, but maybe is nice enough. Creating an instance will be as ugly as writing
MyBunch2<List<String>, String> a = new MyBunch2<List<String>, String>(String.class);
but you can improve it with a factory method
public static <M2> MyBunch2<List<M2>, M2> of(Class<M2> individualType){
return new MyBunch2<List<M2>, M2>(individualType);
}
and then write
MyBunch2<List<String>, String> b = MyBunch2.of(String.class);
If you are using eclipse, code assist will help you writing the ugly class MyBunch2, String>
Of course, in runtime, this.type will be java.util.List, not java.util.List
To get it right, go for TypeToken.
---Continuation---
You can even make another class
public static class MyBunch3<M> extends MyBunch2<List<M>, M>
{
public MyBunch3(Class<M> individualType) {
super(individualType);
}
}
And then create instances as
MyBunch3<String> c = new MyBunch3<String>(String.class);
There must be a way to do that in just one class...but I can't figure it out
My goal is to develop a class that can output an object of a specified class.
public class GetMe<T> {
public T get() {
Object obj = generateObject();
return (T) obj;
}
}
Now, I know this isn't possible due to erasure. So, we can pass in a class instance and use that to cast.
public class GetMe<T> {
public GetMe<T>(Class<T> clazz) {
this.clazz = clazz;
}
public T get() {
Object obj = generateObject();
return clazz.cast(obj);
}
}
This works great! As long as the class isn't parameterized. If it is, then I've got a problem.
I'm not allowed to use List<String>.class. If I pass in a ParameterizedType (which in itself is difficult to generate), there's no cast method to use.
Is there a way out of this quagmire?
I think super type tokens may solve this problem for you.
The problem with List<String> is that, because of erasure, it would at runtime indistinguishable from any other List<?>. The easiest way around this is to create a new class or interface which has the generic part "fixed", like
public interface StringList extends List<String> {
/* nothing to see here */
}
This way you have a type token (the StringList.class object) which you can pass around at runtime and specifies exactly what you want, but without the need for generics at runtime.
Here is just a small idea. I'm not really sure if it will fit in your context but nevertheless:
public class GetMe<T>
{
public List<T> getList() {
#SuppressWarnings("unchecked")
List<T> result = (List<T>) new LinkedList();
return result;
}
}
Cheers!
The first problem is how you plan to instantiate a List object. If you disclose more of what you are trying to build, we may be able to help you better.
You may want to use Type instead of Class. Type can represent all generic types, although it's not pleasant to work with.
abstract public class GetMe<T>
{
Type type;
public GetMe<T>(Type type)
{
this.type = type;
}
}
Another problem is how to create a generic type like List<String>. The "super type token" looks neat in syntax, in reality it's basically
static class XX extends TypeReference<List<String>>{}
....
Type typeListString = Util.extract(XX.class);
I would much prefer this way
List<String> f;
Type typeListString = getDeclaredField("f").getGenericType();
Actually, many of these frameworks that do fancy runtime generic magics are working on instance fields only.
I think the confusion comes from the fact that you're trying to create an object from List<> which in face it an interface, not an object.
So no matter what you'd try, you just can't create an instance of List<> , (interfaces aren't actual classes, and don't have constructors)
Try using a constraint to avoid having interfaces put in the declaration:
public class GetMe<T extends Object>
This will guarantee that T is an actual class and not an interface.