Difficulty understanding wildcards
public static void reverse(List<?> list)
{
List<Object> tmp = new ArrayList<Object>(list);
for (int i = 0; i < list.size(); i++)
{
list.set(i, tmp.get(list.size()-i-1)); // compile-time error , why ?
}
}
I am studying generics. I know this: when is the wildcard used? is replaced by the appropriate type. When is reverse () called? will be replaced, and since every type is a subtype of Object, there should be no error. I'm looking for a crystal clear explanation. Please, help.
source to share
You can pass any of List<SomeType>
your methods reverse
as an argument List<?> list
, and the compiler only has to add items SomeType
to that List
.
For example, it could be List<String>
, a List<Ineger>
, etc.
Hence, list.set
cannot work as the compiler doesn't know the type of the element that supports the actual List<?> list
one passed to the method. It doesn't know that the elements List<Object> tmp
arose from the same List
(and therefore can be added to it safely).
The correct way to write your method is to define the generic type of the parameter:
public static <T> void reverse(List<T> list)
{
List<T> tmp = new ArrayList<> (list);
for (int i = 0; i < list.size(); i++) {
list.set(i, tmp.get(list.size()-i-1));
}
}
Now the compiler knows that List
they tmp
contain elements of the same type.
source to share
Here:
public static void reverse(List<?> list)
since we don't know what the element's type means list
, we cannot add objects to it.
Here:
List<Object> tmp = new ArrayList<Object>(list);
...
list.set(i, tmp.get(list.size()-i-1)); // compile-time error , why ?
you add Object
to list
, which in the absolute may contain elements with a different type.
In your case, this is not how you write:
List<Object> tmp = new ArrayList<Object>(list);
But the compiler doesn't throw exceptions when trying to understand the logic of your code.
Because otherwise it has to check all the code and the compiler error messages can be very tricky.
For example, let's say you are add tmp.set(0, "aa");
in your code at a time.
source to share
List<?>
is a list of unknown type, we do not know which type is in this list, generics have been introduced to exclude runtime exceptions and ensure security. You cannot add any type to the collection with undefined because you will break it. An example, if allowed:
List<Dog> dogs = new ArrayList<>(Arrays.asList(new Dog("dog")));
public doSomething(List<?> notKnownType) {
notKnownType.add(new Cat("cat"));
// if this ok or not? <?> is not known type, all types have Object as parent let add Cat
}
for (Dog dog : dogs) {
System.out.println(dog); //ClassCastException
}
ClassCastExceptin because we added cat to dogs and threw every dog ββto cat, at runtime the generators do not exist and all collections, etc. contain Object. This line will look like this after compilation:
for (Object dog : dogs) {
System.out.println((Dog) dog); //ClassCastExceptin
}
to prevent this you won't be able to add something to an unknown collection of types and get a compile-time error here list.set(i, tmp.get(list.size()-i-1)); // compile-time error , why ?
source to share