I want to create a list from a Class variable.
...
Class clazz = someObject.getClass();
List<clazz> myList = new ArrayList<clazz>(); // this line is a compilation error
...
How I can do it?
EDIT: I think you are getting it wrong. For example, if someObject is a Car, then myList type should be List<Car>. I want to treat my clazz variable as a type, in some way.
EDIT:
As Boris the Spider pointed out in the comments it actually is possible by using a generic method, but with a slight modification:
Instead of using the normal Class object, use the generic version Class<? extends [Type of someObject]>.
Example:
public static void main(String[] args){
Test t = new Test();
Class<? extends Test> testClass = t.getClass();
List<? extends Test> list = createListOfType(testClass);
}
private static <T> List<T> createListOfType(Class<T> type){
return new ArrayList<T>();
}
Of course you can also just go with
public static void main(String[] args){
Test t = new Test();
List<? extends Test> list = createListOfType(t);
}
private static <T> List<T> createListOfType(T element){
return new ArrayList<T>();
}
OLD POST:
You can't.
You can't, because Java needs to know the generic type you want to use for the ArrayList at compile time. It's possible to use Object as type though.
You can't use variable name as the type. You must use Class instead of clazz
List<Class> myList = new ArrayList<Class>();
Keep in mind about type erasure. The Obvious answer is,
List<Class> myList = new ArrayList<Class>();
You can only specify the type of Object (for compiler). You can't see them at Run Time :)
Related
For example, say I want a Map<Class<?>, List<?>>, so I can put in a class and get out a list of that type - is there something I can replace the question marks with to make that happen?
You can do the trick if you delegate type check to the method:
private class TypedMap {
private Map<Class<?>, List<?>> map = new HashMap<>();
public <T> void put(Class<T> key, List<T> value) {
map.put(key, value);
}
#SupressWarnings("unchecked")
public <T> List<T> get(Class<T> clazz) {
return (List<T>) map.get(clazz);
}
}
Wildcard ? in map declaration does not ensure that key Class<?> and value List<?> would be of the same type. Method put() ensures that. You can not declare map as Map<Class<T>, List<T>> if your class is not generic - that's why you have to use a method.
Get method is unchecked. The cast is safe if entries are added with put() method. (There's still a problem with raw types - but this is unavoidable)
You can add more methods to TypedMap class, but remember about this restrictions.
public static void main(String[] args) {
TypedMap map = new TypedMap();
List<Cat> cats = new ArrayList<>();
List<Dog> dogs = new ArrayList<>();
adder.put(Cat.class, cats);
adder.put(Dog.class, dogs);
adder.put(Cat.class, dogs); // compilation error
}
Java doesn't completely enforce this, but one way to at least get a warning about it, is by using encapsulation:
public class MyClass {
// private, private, private
private Map<Class<?>, List<?>> myMap;
public <T> void put(Class<T> clazz, List<T> list) { // both must have the same T.
myMap.put(clazz, list);
}
...
}
You can still break this by doing something like:
MyClass mc = new MyClass();
Class c = Main.class;
List<String> l = new ArrayList<String>();
mc.put(c, l);
But you'll at least get a warning about unchecked conversion of c to Class<String>. And the unchecked invocation of MyClass::put
Not sure what you're trying to accomplish here, but Map<Class<T>, List<T>> would be the closest thing. The T is one single class type, though, so you can't put multiple classes into one Map.
You'll get a ClassCastException if you try to put objects of different classes into the same Map.
I apologize in advanced for my lack of knowledge in generics... I am trying to understand how generics work and I am not sure what it is but I am missing a key part that is eluding me so hopefully someone can clarify a little more and get me over this hump.
BaseDtoUtil.mapToDto(map, OcrImageDocDto.class); //OcrImageDocDto extends DtoUtils
//This method is in class BaseDtoUtil
public static <T> List<T> mapToDto(Map<String, Object> map, Class<? extends DtoUtils> klass) throws SparkSQLException{
T obj = (T)klass.newInstance();
//return a list of these
}
So I guess there are two questions I have:
First is why does it complain when I pass in OcrImageDocDto.class when the variable defined for the method call is any class? (I originally had it as (Class<?>) Doesn't that mean any class value? Obviously I am wrong but not sure what it means then.
Second question is if I send in a class value am I actually able to get that instance and return a type value back? <T> List<T>? If I am not mistaken I believe that the generic variables <T> List<T> is used for instantiations of the object. But what do they do if it is a static method?
I am a bit lost and maybe the way I understand generics is wrong... So if someone can clear these two up I think it will help a lot!
Question 1:
public static <T> List<T> mapToDto(
Map<String, Object> map,
Class<? extends DtoUtils> klass) throws SparkSQLException{
T obj = (T)klass.newInstance();
...
You don't know that klass.newInstance() can be cast to a T - all you know is that it is an instance of DtoUtils (or a subclass).
As such, you can either change to use:
DtoUtils obj = klass.newInstance();
or constrain T to extend DtoUtils:
public static <T extends DtoUtils> List<T> mapToDto(
Map<String, Object> map,
Class<? extends T> klass) throws SparkSQLException{
T obj = klass.newInstance();
...
Question 2:
Yes, because you have an actual instance of the class. You would not be able to create an instance without that (or some other object which can provide instances of T), because of type erasure.
This would not work:
public static <T extends DtoUtils> List<T> mapToDto(
Map<String, Object> map) throws SparkSQLException{
T obj = new T(); // Compiler error.
Something like the following works just fine:
T obj = klass.newInstance();
List<T> list = new ArrayList<>();
list.add(obj);
return list;
I am trying to create a function that would take an object of a class and then the function will use that object to retrieve it class and use it as a model for XML parsing. The function looks like this:
myClass myClassObject=new myClass();
List aList = parseXMLtoList(stringBuffer.toString(),myClassObject);
private List parseXMLtoList(String xmlString,Object modelClassObject) {
List<modelClassObject.getClass()> xmlList = new ArrayList(); //modelClassObject cannot be resolved to a type
...
But when I try to use reflecting to given class dynamically it gives me this error: modelClassObject cannot be resolved to a type
How can achieve this?
Don't think you can implement it. Types of collections as other such stuff is for compile time only. So just use List.
You must use generics like this:
MyClass myClassObject= new MyClass();
List<MyClass> aList = parseXMLtoList("<some_xml/>", myClassObject);
private static <T> List<T> parseXMLtoList(String xmlString, T modelClassObject) {
List<T> xmlList = new ArrayList<T>();
You can restrain the type that can be used by adding this in the generic definition:
private static <T extends MyClass>
More on generics : https://docs.oracle.com/javase/tutorial/java/generics/
Take a look at the next code:
ArrayList<? extends Object> list = new ArrayList<Object>();
list.add(new Object());
It does not compile. The question is why?
ArrayList<? extends Object>
is the same as just
ArrayList<?>
and you can assign any ArrayList to a variable of this type. For example,
ArrayList<? extends Object> list = new ArrayList<String>();
is legal. Clearly, the language semantics will not let you add an Object to such a list. In fact, they won't let you add anything at all to it, except null, or something which you directly retrieved from the list.
As noted by Lukas in the comment, it is far from trivial to even add the list's own item back to it: you need a helper method to capture the wildcard into a named type.
public static void main(String[] args) {
ArrayList<? extends Object> list = new ArrayList<String>();
addOwn(list);
}
static <T> void addOwn(List<T> l) { l.add(l.get(0)); }
The problem is that Foo extends Object does not mean Collection<Foo> can be treated as a subtype of Collection<Object>. This is simply because the former class does not permit you to do everything the latter does; for instance, you cannot add an Object to a Collection<Foo>. Using generics instead of some concrete class Foo doesn't change this.
I think the reason is because Generic types are not polymorphic. When you use wildcards ? with extends you cant add anything in the collection except null.
Here is an example to what will happen if that is allowed:
Class Car{
}
class A extends Car {
}
class B extends Car{
}
Now you have List<? extends Car>
public void someMethod(List<? extends Car> list){
list.add(new A()); //this is not valid
}
Also you may invoke the method like this:
List<B> someList = new Array:list<B>();
somemethod(someList);
Simply:
public static class MyClass<T> {
// i don't want to keep an instance of T, if it is not necessary.
// and it is not nice, not neat.
// Or, let's say, the member are in the form of :
ArrayList<T> mArrayList = new ArrayList<T>();
// the problem of getting the generic type parameter is still present.
}
#Test
public final void test() {
MyClass<Integer> myObject = new MyClass<Integer>();
getParamType( myObject );
}
private static final <T> void getParamType(final MyClass<T> _myObject) {
System.out.println(_myObject.getClass().getTypeParameters()[0]); // T
System.out.println(((T) new Object()).getClass()); // class java.lang.Object
}
How to let the code print class java.lang.Integer?
i know quite a few of stackoverflow threads are asking (and answering) about this. Yet they couldn't solve this question.
i don't know why some need to call getGenericSuperclass() - as there is no inheritance involved in this simple case.
And i can't cast it to ParameterizedType as well.
.
System.out.println((ParameterizedType) _myObject.getClass());
// Compile Error: Cannot cast from Class<capture#11-of ? extends TreeTest2.MyClass> to ParameterizedType
System.out.println((ParameterizedType) _myObject.getClass().getGenericSuperclass());
// Runtime Exception: java.lang.ClassCastException
Based on #Thomas's guide, i have found a work-around way to get class java.lang.Integer.
First, we create an anonymous (it need to be anonymous) sub-class of MyClass<T> in the testing code. (Which is weird. Why it only support sub-classes?)
#Test
public final void test() {
MyClass<Integer> myObject = new MyClass<Integer>() {}; // Anonymous sub-class
getParamType( myObject );
}
Then we can use the getGenericSuperclass() method to get a Type then cast it to ParameterizedType and afterwards uses getActualTypeArguments():
private static final <T> void getParamType(final MyClass<T> _myObject) {
System.out.println( ((ParameterizedType) _myObject.getClass().getGenericSuperclass()).getActualTypeArguments()[0] );
}
It perfectly prints class java.lang.Integer.
This is not-so-good because the testing codes should simulate the actual situation, where users most likely won't keep creating meaningless sub-classes.
This approach is based on the idea of the TypeReference class. But i don't really know how to use it. I have tried class MyClass<T> extends TypeReference<T>. But i still have to create sub-class of MyClass<T> to have TypeReference.getType() prints class java.lang.Integer.
Please help, and thanks for any inputs, as the best approach is not here yet.
A further question based on the above workaround: Why only anonymous sub-class works?
public static class SubMyClass<T> extends MyClass<T>{}
#Test
public final void test() {
MyClass<Integer> myObject = new MyClass<Integer>() {}; // Anonymous sub-class
getParamType( myObject ); // class java.lang.Integer
MyClass<Integer> mySubObject = new SubMyClass<Integer>(); // named sub-class
getParamType( mySubObject ); // T
}
(MyClass and getParamType() unchanged.)
This is sort of difficult, because Java deliberately can't do that ("type erasure").
The work-around is called super type tokens. There are also some threads on SO about that (like this one or that one).
When you have a question like this, you should ask yourself, how would you do it without Generics? Because any Generics program can be converted into an equivalent program without Generics. (This conversion is called type erasure.) So if you cannot do it without Generics, you cannot do it with Generics either.
Your program without Generics looks like this:
public static class MyClass {
ArrayList mArrayList = new ArrayList();
}
#Test
public final void test() {
MyClass myObject = new MyClass();
Integer result = getParamType( myObject ); // how would you implement getParamType()?
}
Java has a misguided feature called Type Erasure that specifically prevents you from doing that.
Generic parameter information does not exist at runtime.
Instead, you can manually accept a Class<T>.