13 Replies Latest reply: Feb 8, 2010 12:28 PM by 843793 RSS

    Generics compiler error explanation required

    843793
      Can someone help explain why the following doesn't compile/isn't allowed:
      interface Bar<V> {
      }
      
      interface Foo<V> {
           void addAll(Set<Bar<? extends V>> theBar);
      }
      
      Foo<String> foo = null;
      Set<Bar<String>> bars = null;
      
      foo.addAll(bars);
      The compiler gives something like
      addAll(java.util.Set<Bar<? extends java.lang.String>>) in Foo<java.lang.String> cannot be applied to (java.util.Set<Bar<java.lang.String>>)

      I'm using java 1.6.0_18-b07 for Windows x64

      The real life example is taking a Set of Map.Entry from the Map.entrySet method; I am stumped as to why this isn't allowed by the compiler, I have a workaround but I'd like to understand why this isn't valid.
        • 1. Re: Generics compiler error explanation required
          843793
          Hello Bob,

          it's not allowed because Set<String> is not equal to Set<? extends String>, it is just a subtype. I suspect you are aiming for the following:
          interface Foo<V> {
               <W extends V> void addAll(Set<Bar<W>> theBar);
          }
          With kind regards
          Ben
          • 2. Re: Generics compiler error explanation required
            EJP
            it's not allowed because Set<String> is not equal to Set<? extends String>, it is just a subtype.
            It's not even a subtype. It is a distinct type.
            • 3. Re: Generics compiler error explanation required
              843793
              ejp wrote:
              it's not allowed because Set<String> is not equal to Set<? extends String>, it is just a subtype.
              It's not even a subtype. It is a distinct type.
              I fail to see how you reach that conclusion, I must be overlooking something.

              With kind regards
              Ben
              • 4. Re: Generics compiler error explanation required
                EJP
                it is just a subtype
                If that means that Set<? extends String> extends Set<String>, it is incorrect.

                If it means something else please explain.
                • 5. Re: Generics compiler error explanation required
                  843793
                  ejp wrote:
                  it is just a subtype
                  If that means that Set<? extends String> extends Set<String>, it is incorrect.

                  If it means something else please explain.
                  I meant to express Set<String> <: Set<? extends String>.

                  With kind regards
                  Ben
                  • 6. Re: Generics compiler error explanation required
                    EJP
                    'Subtyping does not extend through generic types: T <: U does not imply that C<T> <: C<U>.'

                    [JLS #4.10|http://java.sun.com/docs/books/jls/third_edition/html/typesValues.html#4.10]
                    • 7. Re: Generics compiler error explanation required
                      843793
                      ejp wrote:
                      'Subtyping does not extend through generic types: T <: U does not imply that C<T> <: C<U>.'

                      [JLS #4.10|http://java.sun.com/docs/books/jls/third_edition/html/typesValues.html#4.10]
                      Now I am thoroughly confused. That is supposed to convey that List<Integer> is not a subtype of List<Number> even though Integer is a subtype of Number. In other words Java generics are invariant.
                      The relevant sections are [4.10|http://java.sun.com/docs/books/jls/third_edition/html/typesValues.html#4.10], [4.10.2|http://java.sun.com/docs/books/jls/third_edition/html/typesValues.html#4.10.2] and [4.5.1.1|http://java.sun.com/docs/books/jls/third_edition/html/typesValues.html#4.5.1.1]. Specifically:
                      The direct supertypes of the type C<T1,...,Tn> , where Ti,1&#8804;i&#8804;n, is a type, are
                      - D<U1 theta, ..., Uk theta>, where D<U1,...,Uk> is a direct supertype of C<F1,...,Fn>, and theta is the substitution [F1 := T1, ..., Fn := Tn]
                      - C<S1,...,Sn> where Si contains (§4.5.1.1) Ti for 1&#8804;i&#8804;n.
                      Granted, there is an error in the spec, but unless you believe "[...] are D<U1 theta, ..., Uk theta>, where C<S1,...,Sn> where Si contains [...]" is not gibberish, it's an obvious error.

                      In our case C = Set, T1 = String, n = 1 and one of the direct supertypes is C<S1> where S1 = "? extends String". This is true because String is contained by (<=) "? extends String" as defined by 4.5.1.1:
                      T <= ? extends T
                      Now we have determined that Set<? extends String> is a direct supertype of Set<String>, the rest can be found in the preface to 4.10:
                      The supertypes of a type are obtained by reflexive and transitive closure over the direct supertype relation [...]
                      and
                      The subtypes of a type T are all types U such that T is a supertype of U [...]
                      With kind regards
                      Ben
                      • 8. Re: Generics compiler error explanation required
                        EJP
                        Well the compiler agrees with me ;-)
                        class Generic<T>
                        {
                             public void     put(T object, Set<String> set) {}
                             
                             // The type of this 'set' is not a subtype of the previous one.
                             public void     useit(T object, Set<? extends String> set)
                             {
                                  put(object, set);     // ======> compilation error
                             }
                        }
                        • 9. Re: Generics compiler error explanation required
                          843793
                          ejp wrote:
                          Well the compiler agrees with me ;-)
                          class Generic<T>
                          {
                               public void     put(T object, Set<String> set) {}
                               
                               // The type of this 'set' is not a subtype of the previous one.
                               public void     useit(T object, Set<? extends String> set)
                               {
                                    put(object, set);     // ======> compilation error
                               }
                          }
                          If you reread the thread, you will notice that noone ever asserted that Set<? extends String> is a subtype of Set<String>, but the other way around. Miraculously the compiler agrees with that as well:
                          public void     put(T object, Set<? extends String> set) {}
                          
                          // The type of this 'set' is a subtype of the previous one.
                          public void     useit(T object, Set<String> set) {
                               put(object, set);     // compiles fine
                          }
                          With kind regards
                          Ben
                          • 10. Re: Generics compiler error explanation required
                            843793
                            Hi Guys,

                            Thanks for your help; I am still unsure as to why
                            public <W extends V> void addAll(Set<Bar<W>> theBar);
                            is allowed and
                            public void addAll(Set<Bar<? extends V>> theBar);
                            isnt.

                            I'm not a total noddy, but I just can't see it and it's one of those things that's going to bug me till I understand it. Any further guidance would be much appreciated. Thanks for your help so far.

                            Bob.
                            • 11. Re: Generics compiler error explanation required
                              843793
                              Hello Bob,

                              sorry, we went off on a tangent there. I think this [blog post by Peter Ahé|http://blogs.sun.com/ahe/entry/why_is_the_capture_of] does a very good job explaining it. It also makes a case for
                              public void addAll(Set<? extends Bar<? extends V>> theBar);
                              which is probably preferable for a public API (because the type parameter W in my suggestion does you no good and superfluous type parameters should be avoided).

                              With kind regards
                              Ben
                              • 12. Re: Generics compiler error explanation required
                                843793
                                Hi Ben,

                                Thank you very much for that. So if I'm understanding correctly it's because of type conversion that it's not allowed?

                                I'm still unsure as to why it's not allowed, can't get my head round the "Set<? extends A>" is not a "Set<A>". But at least my API is usable now, many thanks.

                                Bob.
                                • 13. Re: Generics compiler error explanation required
                                  843793
                                  Hello Bob,

                                  I'm a little pressed for time so I'll keep this short.

                                  A List<Integer> is not a List<Number>, here is why:
                                  List<Integer> ints = new ArrayList<Integer>();
                                  List<Number> nums = ints; // this is illegal, assume it isn't
                                  nums.add(Double.valueOf(1.23));
                                  Integer i = ints.get(0); // ClassCastException
                                  Now a List<Integer> is a List<? extends Number> because Integer extends Number and thus satisfies the wildcard bounds. We can deduce that a List<? extends Number> can therefore not always be a List<Number>:
                                  List<Integer> ints = new ArrayList<Integer>();
                                  List<? extends Number> temp = ints; // legal
                                  List<Number> nums = temp; // this is illegal, assume it isn't
                                  nums.add(Double.valueOf(1.23));
                                  Integer i = ints.get(0); // ClassCastException
                                  So to summarize: It's not allowed because it would leave holes in the type system.

                                  With kind regards
                                  Ben