5 Replies Latest reply: Jun 7, 2012 8:06 AM by 800357 RSS

    Generic subtyping

    800357
      Hi,

      Given this class declaration:
      public class QMap<T> implements QFunction<T> {
           
           protected List<Option<? super T>> optionPool;
           
           public QMap( Option<? super T> o ) { ... }
           
           public QMap(List<Option<? super T>> optionList) { ... }
      ...
      }
      This (snippet 1) works:
        Option<Number> o;
        QMap<Double> testMap = new QMap<Double>( o );
      But this (snippet 2) doesn't:
        List<Option<Number>> testPool = new ArrayList<Option<Number>>();
        QMap<Double> testMap = new QMap<Double>( testPool );
      Compiler says: The constructor QMap<Double>(List<Option<Number>>) is undefined.

      I can fix it like so (snippet 3):
                List<Option<? super Double>> testPool = new ArrayList<Option<? super Double>>();
                QMap<Double> testMap = new QMap<Double>( testPool );
      But I thought snippet 2 should work, and moreover declaring testPool like in snippet 3 annoys me because I know what kind of Options I will put in there just fine. I can see why a List<Option<Number>> is not the same as a List<Option<? super Double>> nor a subtype of it, since a List<Option<? super Double>> could also be a List<Option<Object>>, for instance. Ok I guess I understand it on a practical level, but why was it designed this way? It doesn't make any sense.
        • 1. Re: Generic subtyping
          936584
          Hi matt32,

          You could change your constructor this way:
               public QMap(List<? extends Option<? super T>> optionList) {
                    
               }
          And now you can use your snippet 2 as you expected:
          List<Option<Number>> testPool = new ArrayList<Option<Number>>();
            QMap<Double> testMap = new QMap<Double>( testPool );
          But I don't completely understand why this works this way, like you, I'll wait for some answer that clarifies this.
          • 2. Re: Generic subtyping
            800357
            Thanks for that, although I also still don't understand why that should work and the original constructor not. Any further input on this would be appreciated.
            • 3. Re: Generic subtyping
              800357
              An update. This also doesn't work:
              List<Option<Double>> testPool = new ArrayList<Option<Double>>();
                QMap<Double> testMap = new QMap<Double>( testPool );
              Compiler says:
              The constructor QMap<Double>( List<Option<Double>> ) is undefined
              Surely this should work?? Is this a bug? I'm using JDK 7u4 in Eclipse Indigo.
              • 4. Re: Generic subtyping
                805574
                No that shouldn't work. The <? extends T> vs <? super T> can be very tricky.

                The place to start would be the generics tutorial, specifically wildcards and more wildcards.
                • 5. Re: Generic subtyping
                  800357
                  Okay, I thought about this some more and I get it now; it's basically because of what I said in my original post. The reason it doesn't work is the same as the reason why
                   List<Object> lo; List<String> ls; lo = ls; 
                  doesn't work. I could add random stuff into lo, which wouldn't be Strings. Therefore I need to declare
                  List<? extends Object> lo;
                  Now lo has become read-only and I can work with it without any risk.

                  Same for my case:
                  List<Option<? super Double>> l1;
                  List<Option<Number>> l2;
                  l1 = l2; //compiler error: type mismatch
                  Because I could add an Option<Object> into l1, or any other Option<supertype of Double> that is not an Option<Number>. Therefore I need to declare
                  List<? extends Option<? super Double>> l1;
                  Note that
                  List<? super Option<? super Double>> l1;
                  doesn't work, although I'm not sure why (since an Option is both a sub and supertype of an Option).