Java generics, objects and wildcards differences & clarifications - java

I wish to understand this concept:
T object - generic, will be erased into actual type.
? object - will be erased into what?
Object object;
What are the differences between T, ? and Object?
I can easily understand #1, but what about:
Object var;
? var;
What is the difference between the two? I have read that I can't use ? explicitly, like T or any other variable, and that ? is related to objects, not types.
But what is the practical reason? Why can't I just write a List of objects (List<Object>) instead of a List of wildcards (List<?>)? As I don't know the types of objects on both cases.
In addition, I would like to know what is the erasure for ? ?

I will list the main differences between T and ?:
Basic: T is a type parameter and ? is a wildcard.
Meaning: T is used as a type parameter when defining a generic class. T will be replaced by a concrete type when you instantiate the generic class.
On the other hand, we use ? when we want to refer to an unknown type argument.
Place of definition: You need to declare T on the top of the class, or method if you define a generic method. You can use ? everywhere.
Mapping: Every use of T is mapped to the same type (in the same class). Every use of ? can be mapped to a different type.
Object instantiation: You can create objects with the generic parameter T like new ArrayList<T>(). You cannot instantiate objects but only pointers with ?.
Collections updating: You can add objects to a collection of type T. You cannot add object to a collection of type ? (since you don't know its type).
Type erasures: With generics, type erasure applies to the use of generics. when generics are used, they are converted into compile-time checks and execution-time casts. So if you have this code for example: List<String> myList = new ArrayList<String>(); and then you wish to add to your list so you do myList.add("Hello World"); and then you want to get the item you just added by performing String myString = myList.get(0); then the compiler will compile your code to List myList = new ArrayList(); and String myString = (String) myList.get(0); (the add stays the same for obvious reasons).
So basically, at execution time there is no way of finding out that T is essentially String for the list object (that information is gone).
Now for wildcards the story is different. A wildcard (?) is replaced by an Object (since it's unbounded). This is not very useful. At build-time the compiler will check you are only calling Object's behaviours. If you have something like ? extends Foo, then the ? is replaced with its bound Foo (at build-time the compiler will check you are only passing Foo or any of its subtypes (types that inherit from Foo) as an argument).
For differences between ? and Object & T and Object you may read here and here respectively.

Related

Java Generics with class object as generic type

I want to create a Java Object for a class that is defined using generics.
Specifically, i want to create a List of Objects of class that is determined at runtime.
I would want something like
Class clazz = Class.forName("MyClass");
List<clazz> myList = new ArrayList<>(); // This isn't allowed
Defining an array of object would allow me to store a list of MyClass type objects, but that would lead to casting the objects every-time the object is fetched from the list, i would like to avoid such a scenario.
Is there a way to achieve something like the above code using java.
Well, since you know that class, you could (with a warning) cast the List itself; but you would still need to know the class name and some checks for that, like for example:
if(clazz.getName().equals("java.lang.String")) {
// warning here
yourList = (List<String>) yourList;
}
As I understand your question, you want to know if is possible for the compiler to know the runtime type while it is limited to the compile type.
You can't. And your hypothesis is also wrong:
Defining an array of object would allow me to store a list of MyClass
type objects, but that would lead to casting the objects every-time
the object is fetched from the list, i would like to avoid such a
scenario.
In Java, generics does not remove the cast: it is still there in the form of type erasure and (hidden) cast. When you do List<String>, you merely ask the compiler to hide the cast in operation such as T get(int): there will be a cast to String.
If you want to use the compile time information, than that would mean you already have/know the type MyClass available at compile time and you would not use Class::forName but MyClass.class which would return a Class<MyClass>.
What you can do is either:
Use an interface if you have a common ground for theses classes (like JDBC Driver).
Cast the raw list into a known type, for example using Class::isAssignableFrom.
No, you can't. Generics in Java is just a compile-time type checking mechanism. If you don't know the type until runtime, then, obviously, it cannot be used for compile-time type checking. The compiler can't determine at compile time what types to allow you to put into or get out of a List<clazz>, so it's no more meaningful than just a raw type List.
There is a little trick that can be used to work with a specific unknown type: Declare a type parameter that is only used for the unknown type:
public <T> void worksWithSomeUnknownClass() throws ReflectiveOperationException {
#SuppressWarnings("unchecked")
Class<T> clazz = (Class<T>) Class.forName("MyClass");
T obj = clazz.getConstructor().newInstance();
List<T> myList = new ArrayList<>();
myList.add(obj);
}
This solution is very limited though. It makes sure that you don't mix it up with other unknown types or Object, but you can not really do anything with T. And you have to declare a type parameter on every method that uses it.

Java reflection get runtime type when using generics

I am wondering how can I get the runtime type which is written by the programmer when using generics. For example if I have class Main<T extends List<String>> and the programmer write something like
Main<ArrayList<String>> main = new Main<>();
how can I understand using reflection which class extending List<String> is used?
I'm just curious how can I achieve that. With
main.getClass().getTypeParameters()[0].getBounds[]
I only can understand the bounding class (not the runtime class).
As the comments above point out, due to type erasure you can't do this. But in the comments, the follow up question was:
I know that the generics are removed after compilation, but I am wondering how then ClassCastException is thrown runtime ? Sorry, if this is a stupid question, but how it knows to throws this exception if there isn't any information about classes.
The answer is that, although the type parameter is erased from the type, it still remains in the bytecode.
Essentially, the compiler transforms this:
List<String> list = new ArrayList<>();
list.add("foo");
String value = list.get(0);
into this:
List list = new ArrayList();
list.add("foo");
String value = (String) list.get(0); // note the cast!
This means that the type String is no longer associated with the type ArrayList in the bytecode, but it still appears (in the form of a class cast instruction). If at runtime the type is different you'll get a ClassCastException.
This also explains why you can get away with things like this:
// The next line should raise a warning about raw types
// if compiled with Java 1.5 or newer
List rawList = new ArrayList();
// Since this is a raw List, it can hold any object.
// Let's stick a number in there.
rawList.add(new Integer(42));
// This is an unchecked conversion. Not always wrong, but always risky.
List<String> stringList = rawList;
// You'd think this would be an error. But it isn't!
Object value = stringList.get(0);
And indeed if you try it, you'll find that you can safely pull the 42 value back out as an Object and not have any errors at all. The reason for this is that the compiler doesn't insert the cast to String here -- it just inserts a cast to Object (since the left-hand side type is just Object) and the cast from Integer to Object succeeds, as it should.
Anyhow, this is just a bit of a long-winded way of explaining that type erasure doesn't erase all references to the given type, only the type parameter itself.
And in fact, as a now-deleted answer here mentioned, you can exploit this "vestigial" type information, through a technique called Gafter's Gadget, which you can access using the getActualTypeArguments() method on ParameterizedType.
The way the gadget works is by creating an empty subclass of a parameterized type, e.g. new TypeToken<String>() {}. Since the anonymous class here is a subclass of a concrete type (there is no type parameter T here, it's been replaced by a real type, String) methods on the type have to be able to return the real type (in this case String). And using reflection you can discover that type: in this case, getActualTypeParameters()[0] would return String.class.
Gafter's Gadget can be extended to arbitrarily complex parameterized types, and is actually often used by frameworks that do a lot of work with reflection and generics. For example, the Google Guice dependency injection framework has a type called TypeLiteral that serves exactly this purpose.

What's the difference between raw types, unbounded wild cards and using Object in generics

I am reading the chapter on Generics in Effective Java.
Help me understand difference between Set, Set<?> and Set<Object>?
The following paragraph is taken from the book.
As a quick review, Set<Object> is a parameterized type representing a
set that can contain objects of any type, Set<?> is a wildcard type
representing a set that can contain only objects of some unknown
type, and Set is a raw type, which opts out of the generic type
system.
What is meant by "some unknown type"? Are all unknown types of type Object? In that case what is the specific difference between Set<?> and Set<Object>?
a raw type (Set) treats the type as if it had no generic type information at all. Note the subtle effect that not only will the type argument T be ignored, but also all other type arguments that methods of that type might have. You can add any value to it and it will always return Object.
Set<Object> is a Set that accepts all Object objects (i.e. all objects) and will return objects of type Object.
Set<?> is a Set that accepts all objects of some specific, but unknown type and will return objects of that type. Since nothing is known about this type, you can't add anything to that set (except for null) and the only thing that you know about the values it returns is that they are some sub-type of Object.
At runtime, the JVM will just see Set because of type erasure.
At compile-time, there's a difference:
Set<Object> parameterized a type E with Object thus, Set.add(E element) will be parameterized to Set.add(Object element).
Set<?> on the other hand, adds a wildcard on a type E so Set.add(E element) is translated to Set.add(? element). Since this is not compilable, java instead "translates" it as Set.add(null element). It means that you cannot add anything to that set (except a null). The reason is that the wildcard is referencing to an unknown type.
what is meant by "some unknown type"
Exactly what it means - the Set has some generic parameter, but we don't know what it is.
So the set assigned to a Set<?> variable might be a Set<String>, or a Set<Integer>, or a Set<Map<Integer, Employee>> or a set containing any other specific type.
So what does that mean for how you can use it? Well, anything you get out of it will be an instance of the ?, whatever that is. Since we don't know what the type parameter is, you can't say anything more specific than that elements of the set will be assignable to Object (only because all classes extend from it).
And if you're thinking of adding something to the set - well, the add method takes a ? (which makes sense, since this is the type of objects within the set). But if you try to add any specific object, how can you be sure this is type-safe? You can't - if you're inserting a String, you might be putting it into a Set<Integer> for example, which would break the type-safety you get from generics. So while you don't know the type of the generic parameter, you can't supply any arguments of this type (with the single exception of null, as that's an "instance" of any type).
As with most generics-related answers, this has focused on collections because they're easier to comprehend instinctively. However the arguments apply to any class that takes generic parameters - if it's declared with the unbounded wildcard parameter ?, you can't supply any arguments to it, and any values you receive of that type will only be assignable to Object.
Set: No generics here, unsafe. Add what you want.
Set<?>: A set of a certain type we don't know from our scope. The same as Set<? extends Object>. Can reference to Sets of any type, but that type must be defined at the point where the set is actually instantiated. With the wildcarded reference, we can´t modify the set (we can't add or remove with anything not null). Is like a view.
Set<Object>: Set containing Objects (base class only, not subclasses). I mean you can instance the set using Collections of type Object, like HashSet<Object> but not with HashSet<String>. You can of course add elements of any type to the set, but just because it happens that everything is an Object or a subclass of Object. If the set were defined as Set, you can only add Numbers and subclasses of Number, and nothing more.
The difference between Set<Object> and Set<?> is that a variable of type Set<?> can have a more specific generic type assigned to it, as in:
Set<?> set = new HashSet<Integer>();
while Set<Object> can only be assigned Set<Object>:
Set<Object> set = new HashSet<Integer>(); // won't compile
The Set<Object> is still useful, since any object can be put into it. It is much like the raw Set in that sense, but works better with the generic type system.
I was explaining this item to my friend and specifically asked for the "safeAdd" method as a counter to the unsafeAdd example. So here it is.
public static void main(String[] args) {
List<String> strings = new ArrayList<String>();
unsafeAdd(strings, new Integer(42)); // No compile time exceptions
// New
safeAdd(strings, new Integer(42)); // Throwing an exception at compile time
String s = strings.get(0); // Compiler-generated cast
}
private static void unsafeAdd(List list, Object o) {
list.add(o);
}
private static <E> void safeAdd(List<E> list, E o) {
list.add(o);
}
Set<?> set = new HashSet<String>();
set.add(new Object()); // compile time error
Since we don’t know what the element type of set stands for, we cannot add objects
to it. The add() method takes arguments of type E, the element type of the Set.
When the actual type parameter is ?, it stands for some unknown type. Any
parameter we pass to add would have to be a subtype of this unknown type. Since we
don’t know what type that is, we cannot pass anything in. The sole exception is
null, which is a member of every type.
Given a Set<?>, we can call get() and make use of the result. The result type is an
unknown type, but we always know that it is an object. It is therefore safe to
assign the result of get() to a variable of type Object or pass it as a parameter
where the type Object is expected.
Let say,you are writing a common method to print the elements appearing in a List. Now, this method could be used for printing Lists of type Integer, Double,Object or any other type. Which one do you choose?
List<Object> : If we use this one, this would help us to print only the elements of type Object. It wont be useful for printing elements belonging to other classes like Double. This is because Generic does not support Inheritance by default and needs to be specified explicitely using 'super' keyword.
// Would only print objects of type 'Object'
public static void printList(List<Object> list) {
for (Object elem : list)
System.out.println(elem + " ");
System.out.println();
}
List<?> : This could help us have a common method for printing any datatype. We could use this method for printing instances of any type.
// The type would really depend on what is being passed
public static void printList(List<?> list) {
for (Object elem: list)
System.out.print(elem + " ");
System.out.println();
}

What are the trade-offs of Object vs ? in generic parameters?

I saw some code using Object instead of wildcard (?) as a parameter of a generic class. That leads to explicit casts in the client code. What are the benefits, resp. trade-offs for such an approach?
Using Object as the type parameter is entirely different from using ?. See these 2 methods:
void frobnicate1(List<?> someList);
void frobnicate2(List<Object> someList);
Both will receive a List and both will receive Object when they get an element from the List but frobnicate1 can be called with a List<String> or even a List<?>, while frobnicate2 can only be called by a List<Object> (or null).
Note, that instantiating a parameterized type can't be done with a wildcard type argument (?).
So the following will not work:
List<?> someList = new ArrayList<?>();
You will have to use Object (or any other non-wildcard type) instead:
List<?> someList = new ArrayList<Object>();
And don't worry about the explicit cast: The JVM can optimize it away (and probably will), since casts of any reference value to Object will always succeed.
I find it helpful to remember it this way:
List<Object> = a list where each element is treated as an instance of Object
List<?> = a list where each element is treated as a specific but unknown subclass of Object

Populating List items in Java via Reflection

I have to populate object with values that are in String format and I have converter to convert String values to whatever values are required. But how can I know type of List items? Is it List<String> or List<Integer>?
Depending on your use case, you can get the Generic type of a List via a few different methods.
This post shows how it can be done for return types of methods, parameters of methods, and field variables
When runtime inspecting a
parameterizable type itself, like
java.util.List, there is no way of
knowing what type is has been
parameterized to. This makes sense
since the type can be parameterized to
all kinds of types in the same
application. But, when you inspect the
method or field that declares the use
of a parameterized type, you can see
at runtime what type the paramerizable
type was parameterized to.
If it's not one of these 3 cases, you cannot get the Generic type due to type erasure.
Java generics are compile time features, List<String> and List<Integer> are same at runtime.. Therefore you can not know component type of a Collection at runtime..
This kind of question asked so many times, there is a way to get component type of Collection at runtime, but you can extend a collection class, while constructing it, you can give component class to the constructor, by this way you can get component type of collection at runtime.. Here is a sample ;
class GenericList<T> extends ArrayList<T> {
private Class<T> componentClasz;
public GenericList(Class<T> clasz){
super();
componentClasz = clasz;
}
public Class<T> getComponentClasz() {
return componentClasz;
}
}
GenericList<String> list = new GenericList<String>(String.class);
The type of the List was erased when the List was compiled.
Also, a String can't just be "converted" into any value willy-nilly. For example of a conversion failure, what's the appropriate Integer value for "hello"?
If you want a list that can hold any Java object, use List<Object>.
You'd have to examine the object(s) of the list, not the list itself. Because Java uses type safe erasure and not reification there's no way to reflect the generic type of the list (or any other Java collection for that matter).

Categories