8 ms·
TIL why class Foo<T> { void test(Foo<T> f) { // ... } static void boom() { Foo<?> f = new Foo<String>();
by _lce0 12y ago
TIL why
class Foo<T> {
void test(Foo<T> f) {
// ...
}
static void boom() {
Foo<?> f = new Foo<String>();
f.test(f); // <--- boom! compile error
}
}
- tel 12y agoI'm assuming it's because the T variable isn't generalized over within the definition of Foo, so you can't use (test) at arbitrary types until Foo is closed?
- salmonellaeater 12y agoIn concrete terms, imagine Foo looks like this: 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 runtime
- peeters 12y agoYour example presents zero unsafety for the usage presented by eridal. The only reason it's considered unsafe is because the compiler isn't sophisticated enough to compare captures of wildcards, and in this case, might not be sophisticated enough to know that `f` isn't being reassigned.
- tel 12y agoI understand the covariance bit, but I think I just fail to understand the Java type semantics more broadly. I'd have to just study it and become more familiar
- narrator 12y agoThe key thing to remember is that generics are just for the compiler. There isn't anything actually embedded in the bytecode that knows what type a generic is.
- nicobn 12y agoThis is only partially true. Generic types are accessible through the reflection API. What you are referring to is type erasure. See for yourself: https://gist.github.com/nicobn/8135faeda058fef5b6b9 https://gist.github.com/nicobn/8135faeda058fef5b6b9
- slantedview 12y agoGenerics are captured in various places in bytecode - most importantly in type definitions. This is how the common type token pattern is exploited. Where generics are erased is in local variable declarations.
- peeters 12y agoThere isn't really a good reason. The compiler just isn't sophisticated enough to support it. 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.
- taeric 12y agoThis is a rude response. The reason is quite reasonable, since there is no way that would work in all cases. A case is easily made where Foo<Number> extends Foo<Integer> makes as much sense as Foo<Integer> extends Foo<Number>. So, which would you have chosen?
- twic 12y agoIt didn't come across as rude to me. And i think it's entirely accurate. 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".
- taeric 12y agoIn the example case, though, it isn't about giving it a name. It is about using the name in the first place. Is no different from: 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.
- KevinEldon 12y agoIf anyone is curious about this, from the FAQ: "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/TechnicalDetails.html#In%20a%20wildcard%20instantiation,%20can%20I%20read%20and%20write%20fields%20whose%20type%20is%20the%20type%20parameter http://www.angelikalanger.com/GenericsFAQ/FAQSections/Techni...?