This code is taken from the OCP textbook. Why do versions 1 and 2 of the addSound method not compile but versions 3 and 4 compile? Compilation error message is "add(capture ) in List cannot be applied to java.lang.String"
public static void main(String[] args) {
List<String> strings = new ArrayList<>();
strings.add("tweet");
List<Object> objects = new ArrayList<>(strings);
addSound(strings);
addSound(objects);
}
//version 1
public static void addSound(List<?> list) {
list.add("quack");
}
//version 2
public static void addSound(List<? extends Object> list) {
list.add("quack");
}
//version 3
public static void addSound(List<Object> list) {
list.add("quack");
}
//version 4
public static void addSound(List<? super String> list) {
list.add("quack");
}
What you need to ask is whether the compiler can be certain that list
's element type is consistent with String
:
<?>
-- the type is unknown. It could be Integer
, for example. We can't know that String
fits. new List<Integer>().add("quack")
wouldn't compile, so neither can this. <? extends Object>
<? extends Object>
-- again, the type is unknown. All classes extend Object
. So again it could be Integer
. <Object>
- No generics here. String
is a subclass of Object
. new List<Object>().add("quack")
works, so this compiles. <? super String>
<? super String>
this is unknown, with the condition (" bounds ") that it's a superclass of of String
. Since we know that String extends Object
, this type could only be Object
or String
. List<Object>().add("quack")
is OK, and List<String>().add("quack")
is OK, so this compiles. The reasoning for number 4, of course applies to more complex type hierarchies:
BufferedInputStream
extends FilterInputStream
extends InputStream
extends Object
. InputStream
implements the interfaces Closeable
and AutoCloseable
.
So <? super FilterInputStream>
<? super FilterInputStream>
matches Object
, InputStream
, FilterInputStream
, Closeable
and AutoCloseable
.
So a List<? super FilterInputStream> l
List<? super FilterInputStream> l
could be a List<Closeable>
or it could be a List<InputStream
>, etc. But all you know for sure is that you can l.add(new FilterInputStream())
or, since it's a subclass l.add(new BufferedInputStream())
In Java you have type safety and type erasure. Both concepts are the reason why version 1 and version 2 are not working.
You can not add anything to List<?>
since you have no clue on what kind of objects are inside the list. It could be possibly be a List of Integer
or String
for example.
public static void addSound(List<?> list) {
list.add("quack"); // does not work since List<?> can be anything - type safety not guaranteed
}
The Java compiler removes all types on run time which is called type erasure eg List<String>
will be List
and List<Integer>
will also be List
- so you can not differentiate them. You might see it in the bytecode but that is irrelevant here. That's the reason why version 2 is not working
//version 2 - has the same type erasure as version 3
public static void addSound(List<? extends Object> list) {
list.add("quack");
}
//version 3
public static void addSound(List<Object> list) {
list.add("quack");
}
The version 2 also does not work because of type safety once again. See this answer for generic bounds
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.