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

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

Related

Problems with Java generics

ArrayList<String> list = new ArrayList<String>();
list = (ArrayList<String>) Files.readAllLines(FilePath);
In the above code, if I remove the explicit casting in second line, the compiler gives an error. Why is this explicit cast required considering the fact that Files.readAllLines(FilePath) returns a List<String>, and ArrayList<String> implements List<String> ?
The method Files.readAllLines() only guarantees to return an object of type List<String>, but not of the more specific type ArrayList<String>. The actual implementation type of the returned list may vary between different JDK implementations (they just need to be sub-classes of List<String>, so while a cast to ArrayList<String> may work in your environment with your JDK implementation it may not work in another.
If you really need your list to be an ArrayList, you can use this code instead:
ArrayList<String> list = new ArrayList<String>();
list.addAll(Files.readAllLines(FilePath));
As you've said, any ArrayList<T> is a List<T>. But the opposite is not true: a List<T> is not always an ArrayList<T>.
This means that if the method readAllLines is returning a List<T>, then you can assign the value to a List<T> variable, not any of its subtypes without an explicit downcast (which should always be avoided).
I don't even see why you would want to downcast it to ArrayList<T>, the less you imply about a type, freer you are.
In your example, you are doing a downcasting, i.e. you want to cast a parent class to a child class. This is very unsafe, since a child may offer more than a parent. In your example, an ArrayList has additional functionalities.
Thus, to ensure safety, the compiler will never allow an implicit downcasting (but upcasting is ok, as a child has necessarily all the functionalities of a parent).

Java generics, objects and wildcards differences & clarifications

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.

adding a list of one type to a list of another type

I have my first list as
List<A> a
I have another list as
List<X.Y.Z> b
How do I add first list to the second one ?
I tried casting -
b.add(List<X.Y.Z>)a) - did not work
Tried adding through iteration of first list - did not work
definitely missed something ?
Unless there is an Inheritance relationship between A and X.Y.Z you cannot have them in the same container because they are not of the same type
You can use the generic superclass Object as the type of the List and this will work.
This is not possible as the reference types for both collections are different. The only way items from one List can be merged with those from another is if they both are of type List<Object> or the types themselves are identical (or at least derived from the same type).
The reason is because of type of List<>
X.Y.Z != A
You can use List<Object>, to which you can add() anything.Even though you added like that
you would have to cast each one back,while getting back.
You need to cast list a as the same type as list b so that they are the same type of object. Check out this article
It should also be noted that if you want to add the elements of List<A> a to List<X.Y.Z> b (which I assume is your intent), rather than the List<A> a itself as an element, you should use the addAll() method, not the add() method. But again, this won't work unless A is a subclass of X.Y.Z, and if A is a super class of X.Y.Z then casting the A variable will only work if it is an instance X.Y.Z.
Either you use List<Object>, to which you can add anything, or you write a method somewhere to convert an object of type A to X.Y.Z.
Notice that, if you use List<Object>, you'll need to cast the object to the desired class when you get it:
List<Object> myList = new List<Object>;
// ...
A myObject = (A) myList.get(0);
X.Y.Z otherObject = (X.Y.Z) myList.get(1);
// ...
Consider the following case
List<Integer> l1=new ArrayList<>();
List<String> l2=new ArrayList<>();
l1.add(2);
l2.addAll((List<String>)l1);
you are trying to do the same thing. here you can't cast integer list to string list.
Same way you can't cast A type list to X.Y.Z. type.

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();
}

Generics (List) typing question

I am trying to use a common technique to create objects from Xml. (Xml is legacy, so although there are already libraries to do this, it seemed faster to write this myself.)
I don't understand the compiler's complaint about the generic usage. Code sample:
public void createObjects() {
List<Object1> objectOnes = new ArrayList<Object1>();
List<Object2> objectTwos = new ArrayList<Object2>();
parseObjectsToList("XmlElement1", objectOnes);
parseObjectsToList("XmlElement2", objectTwos);
}
private void parseObjectsToList(String xmlTag, List<? extends Object> targetList) {
// read Xml and create object using reflection
Object newObj = createObjectFromXml(xmlTag);
targetList.add(newObj)
/* compiler complains: "The method add(capture#2-of ? extends Object) in the type List<capture#2-of ? extends Object> is not applicable for the arguments (Object)"
*/
/* If I change method signature to parseObjectsToList(String xmlTag, List targetList)
it works fine, but generates compiler warning about raw type */
}
Thanks for any enlightenment on the subject!
The problem you are running into is that, with the bounded wildcard that you have defined, you will be unable to add any element to the collection. From this tutorial:
List<? extends Shape > is an example of a bounded wildcard. The ? stands for an unknown type, just like the wildcards we saw earlier. However, in this case, we know that this unknown type is in fact a subtype of Shape. (Note: It could be Shape itself, or some subclass; it need not literally extend Shape.) We say that Shape is the upper bound of the wildcard.
There is, as usual, a price to be paid for the flexibility of using wildcards. That price is that it is now illegal to write into shapes in the body of the method
All a wildcard type means is that the actual type parameter T of the List that you pass as the second argument to parseObjectsToList is going to be a subtype of Object. It does NOT mean that the same List will be parameterized with different types.
So now you have a List<T> (called targetList) and you are trying to call targetList.add(Object). This is illegal because Object is not necessarily a subtype of T.
Because you are adding to the List rather than extracting elements from it, use List<Object> and make sure that's exactly what you pass in.
Using a List<Object> will work, but you might want keep your more precisely typed List<Object1> and List<Object2> for type-safety elsewhere. In that case, you'll need to check the type of each object before adding it to the List.
private void parseObjectsToList(String tag, List<T> list, Class<? extends T> c) {
// read Xml and create object using reflection
Object newObj = createObjectFromXml(tag);
list.add(c.cast(newObj)) ;
}
The cast() operation is a reflective equivalent to the static cast operator: (T) newObj
Using the altered method would look something like this:
parseObjectsToList("XmlElement1", objectOnes, Object1.class);
Think about what you are asking the compiler to do:
Given a list of "something that is a subtype of Object
Let me insert an Object into it
This doesn't make sense. Suppose your list is a list of Integer. Suppose that createObjectFromXml returns a String. It wouldn't make sense to allow inserting a String into a list typed for Integers.
So, your options are either to make your List a List<Object> or to find some way to make createObjectFromXml return a specific type, that you can then tie to the type of your list.

Categories