News for hackers. Stuff that matters.
Re:Java Generics FAQs by _lce0 on Thursday April 16, 2015 @08:26PM (#9391615)
TIL why

    class Foo<T> {

        void test(Foo<T> f) {
            // ...
        }

        static void boom() {
            Foo<?> f = new Foo<String>();
            f.test(f); // <--- boom! compile error
        }
    }
Re:Java Generics FAQs by KevinEldon on Thursday April 16, 2015 @10:31PM (#9391981)
If 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/Techni...?

Re:Java Generics FAQs by peeters on Thursday April 16, 2015 @10:29PM (#9391968)
There 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.
Re:Java Generics FAQs by plesner on Friday April 17, 2015 @12:15AM (#9392239)
Actually, it's not in general decidable whether f.test(f) is safe because f gets read twice and the value might change in the meantime. In the example a simple flow analysis will tell you it doesn't but the general case is equivalent to the halting problem. The generic method solves the problem because now there is only one read in boom() and the concrete type in testFoo ensures that f can't change in an incompatible way between reads.

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.

Re:Java Generics FAQs by twic on Friday April 17, 2015 @03:44AM (#9392747)
> Actually, it's not in general decidable whether f.test(f) is safe because f gets read twice and the value might change in the meantime.

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.

Re:Java Generics FAQs by plesner on Saturday April 18, 2015 @12:20AM (#9398288)
What I said didn't really make sense -- f.test(f) is a concrete example so how am I generalizing? What I meant was: in the general case of expressions that contain multiple references to the same variable it might, for instance

f.testTwo(f = newF, f)

Re:Java Generics FAQs by peeters on Friday April 17, 2015 @10:11AM (#9394373)
First, I wasn't speaking about a general case, I was speaking about the case presented.

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.

Re:Java Generics FAQs by plesner on Saturday April 18, 2015 @12:12AM (#9398275)
The point I don't agree with is that it's really about the compiler. Had it been an optimization that doesn't affect which warnings or errors you get then yes, that's all the compiler. But what you need here is for the types of variables to incorporate flow analysis such that f gets compatible types on both reads if it doesn't change, and incompatible if it does. That would be an incredibly complex type system and people already struggle with how generics in java work.
Re:Java Generics FAQs by taeric on Friday April 17, 2015 @12:06AM (#9392208)
This 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?

Re:Java Generics FAQs by twic on Friday April 17, 2015 @03:40AM (#9392734)
It 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".

Re:Java Generics FAQs by taeric on Friday April 17, 2015 @09:57AM (#9394291)
In 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.
Re:Java Generics FAQs by peeters on Friday April 17, 2015 @10:16AM (#9394398)
I'm both sorry and baffled you found my response rude. As for your question, I'm afraid I don't understand it whatsoever. What am I supposed to be choosing? What does your case have to do with the comment I was replying to?
Re:Java Generics FAQs by tel on Thursday April 16, 2015 @09:21PM (#9391775)
I'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?
Re:Java Generics FAQs by salmonellaeater on Thursday April 16, 2015 @10:21PM (#9391948)
In 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
Re:Java Generics FAQs by peeters on Thursday April 16, 2015 @10:31PM (#9391978)
Your 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.
Re:Java Generics FAQs by tel on Thursday April 16, 2015 @10:32PM (#9391984)
I 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
Re:Java Generics FAQs by narrator on Thursday April 16, 2015 @09:38PM (#9391824)
The 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.
Re:Java Generics FAQs by nicobn on Thursday April 16, 2015 @10:54PM (#9392033)
This 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

Re:Java Generics FAQs by slantedview on Friday April 17, 2015 @01:04AM (#9392341)
Generics 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.