Forum Stats

  • 3,851,385 Users
  • 2,263,969 Discussions
  • 7,904,691 Comments

Discussions

Should/does <-MyInterface> give MyInterface return types?

843793
843793 Member Posts: 41,732 Green Ribbon
edited Jun 2, 2003 2:13PM in Generics
The variance-overview.pdf says that List<-E> has Object as the return type of the get method. If this is true, I'm disappointed. I'd expect the following to work quite happily:
List<-List<*>> myList = buildList();
List<*> elt1 = myList.get(0);
There's no obvious reason why Object should be a supertype of List.

Comments

  • 843793
    843793 Member Posts: 41,732 Green Ribbon
    I'm still a bit fuzzy about contravariance, but it all depends on what your buildList() method returns. I think this is all legal code:
        List<Object> list1 = new ArrayList<Object>();
        List<List<*>> list2 = new ArrayList<List<*>>();
    
        list1.add (new Object());
        list2.add (new ArrayList<Integer>());
    
        List<-List<*>> list3;
        list3 = list2;
        list3 = list1;
        Object o = list3.get(0); // o is not a List!
    Either of list1, list2 or list3 will accept list.add(new ArrayList<Integer>()) - however at the price that nothing can be guaranteed about the type of the contents of the array (in the second case, the list is only guaranteed to contain Object).

    What you want is for your buildList() method to return List<+List<*>>, the variant case (since you are reading from it).
        List<ArrayList<*>> list1 = new ArrayList<ArrayList<*>>();
        List<List<*>> list2 = new ArrayList<List<*>>();
    
        list1.add (new ArrayList<Integer>());
        list2.add (new ArrayList<Integer>());
    
        List<+List<*>> list3;
        list3 = list2;
        list3 = list1;
        List<*> o = list3.get(0); // o is a List
  • 843793
    843793 Member Posts: 41,732 Green Ribbon
    it all depends on what your buildList() method returns.
    It returns a List<-List<*>>, of course. I simply used the method because I had to put something there.

    I think you missed my point, although since I messed up the example that's my fault. What I should have said is that I would expect this to work:
    List<-List<*>> myList = buildList();
    Collection<*> elt1 = myList.get(0);
    The point is that whereas, if I have a List<-ArrayList<*>>, the elements of the list could be Objects, since ArrayList extends Object, I don't see that a List<-List<*>> should contain Objects, since List does not extend Object. If you trace up the inheritance tree of List, you find that it extends Collection, which doesn't extend anything.

    However, I think I'm beginning to see why it's reasonable that an Object be returned. Since you have multiple inheritance of interfaces, get(int) on a List<-C> where C extends A, B could return an A or a B, and the only way to cover that is to make the return type Object.
  • 843793
    843793 Member Posts: 41,732 Green Ribbon
    If you trace up the inheritance tree of List,
    you find that it extends Collection, which doesn't
    extend anything.
    So ... the common base type is indeed Object. To take another few examples
        List<-Integer> myList1 = new ArrayList<Object>(); // Concrete class
        List<-javax.swing.Border> myList2 = new ArrayList<Object>(); // Non-generic interface
        List<-List<*>> myList3 = new ArrayList<Object>(); // Generic interface
    are all legal. And in each case, although I am constrained to assign the most specific type (Integer, Border, List<*>) to the List (since the runtime type may be List<Integer>, etc.) for return values from the Lists I can assume no more than that the values are Objects (since the runtime type may be ArrayList<Object>. The same reasoning applies for your last example of
        List<-C> myList;
        myList = new ArrayList<A>(); // Fine
        myList = new ArrayList<B>(); // Also fine
        myList = new ArrayList<Object>(); // Oops, also fine!
    PS Thanks for helping me sort all this out in my head!
  • 843793
    843793 Member Posts: 41,732 Green Ribbon
    The point is
    that whereas, if I have a List<-ArrayList<*>>, the
    elements of the list could be Objects, since ArrayList
    extends Object, I don't see that a List<-List<*>>
    should contain Objects, since List does not extend
    Object. If you trace up the inheritance tree of List,
    you find that it extends Collection, which doesn't
    extend anything.
    All interfaces do extend Object in a way, because any of Object's methods can be called via an interface:

    List a = new ArrayList();
    if (a.equals(b)) { // this works
    ...
    }

    I remember some fairly dirty paragraphs in the JLS to account for this.

    This behaviour accounted for properly in the very neat Java dialect called Kava, in which interfaces and Objects both inherit from Instance (which is, I think, an interface). See http://researchweb.watson.ibm.com/people/d/dfb/papers/Bacon02Kava.pdf

    Kava looks very neat to me and also supports lightweight classes (i.e. value objects). Supposedly it's backwards compatible with existing Java, so perhaps we'll see some of this one day.
  • 843793
    843793 Member Posts: 41,732 Green Ribbon
    The variance-overview.pdf says that List<-E> has
    Object as the return type of the get method. If this
    is true, I'm disappointed. I'd expect the following to
    work quite happily:
    List<-List<*>> myList =
    buildList();
    List<*> elt1 = myList.get(0);
    There's no obvious
    reason why Object should be a supertype of List.
    Because the variable of type List<-List<*>> might contain
    a reference to an object of type List<Object> at runtime.
This discussion has been closed.