class Foo<T> {
void test(Foo<T> f) {
// ...
}
static void boom() {
Foo<?> f = new Foo<String>();
f.test(f); // <--- boom! compile error
}
} class Foo<T> {
void test(Foo<T> f) {
// ...
}
static void boom() {
Foo<?> f = new Foo<String>();
f.test(f); // <--- boom! compile error
}
}"Assignment to a field of the "unknown" type is rejected because the compiler cannot judge whether the object to be assigned is an acceptable one. Read access to a field of the "unknown" type is permitted, but the field has no specific type and must be refered to through a reference of type Object ."
http://www.angelikalanger.com/GenericsFAQ/FAQSections/Techni...?
You could always write a generic method to do it for you:
static <T> void testFoo(Foo<T> f) {
f.test(f);
}
static void boom() {
Foo<?> f = new Foo<String>();
testFoo(f); // works just fine
}
So the problem isn't that what you're doing is fundamentally unsafe, it's just that Java has no syntax to capture `?` except through a generic type parameter on a method or class.So, which would you have chosen?
Moreover, that pattern - of sending a variable with a wildcard type argument into a method with a type parameter so that the type argument gains a name - is a something i've seen and used many times, and is useful to know. I don't know if there is a name for this technique, but i call it "taming a wildcard".
class Foo {
void test(String arg) {..}
main(..) {
Foo foo = new Foo();
Object s = "sd";
foo.test(s); // Boom
}
}
Note that if that were instead: class Foo {
void test(Number arg) {..}
main(..) {
Foo foo = new Foo();
Long n = 4L;
foo.test(n);
}
}
Then things work, because the subtyping is chosen. In the generics case, there is no relation between Foo<Number> and Foo<Integer>. If there were, then the capture could potentially treat Foo<?> as Foo<Object> and then depending on if you went co or contra, could allow the call.A more sophisticated type system could allow access if it can be proven safe with simple flow analysis but wildcards are already complicated, that would make them much more so for not much benefit.
Might it? I can't see any way that actions in the current thread can change the value of f in between those two uses. Actions in another thread can be ignored until the receiving end of a happens-before relationship is encountered, and there isn't such a point inside that expression.
More generally, yes, two uses of the same wildcarded variable in one method might not yield objects with the same actual type argument. But in this case i think it's safe.
f.testTwo(f = newF, f)
Second, as of Java 8, the compiler knows whether a local variable is "effectively final". In the example given, f qualifies as effectively final. In earlier versions, my point stands that it's simply because the compiler isn't sophisticated enough.
private T value;
void test(Foo<T> f) {
f.value = this.value;
}
You could then assign anything to a member that should only hold a String.This is the same reason why arrays should not be covariant (even though they are in Java). This compiles:
Object[] arr = new Integer[2]; // This should be a compile error, but the language spec allows it
arr[0] = "abc"; // This blows up with an ArrayStoreException at runtimeSee for yourself: https://gist.github.com/nicobn/8135faeda058fef5b6b9