This discussion is archived
3 Replies Latest reply: Sep 17, 2010 12:47 PM by 843793 RSS

Recursive type parameters not allowed?

843793 Newbie
Currently Being Moderated
Hi,

I have two classes which recursivly use each other as type parameter.
Eclipse's compiler simply generates a strange error message, I don't what I should make of it.

Any idea whats going wrong here?
class TableColumn<E, M extends TableModel<E>> {
    public void setDisplayValue(M model, E entry) {
    }
}

class TableModel<E> {
    TableColumn<E, ? extends TableModel<E>> column;

    protected void someMethod(E entry) {

      /*The method getDisplayValue(capture#5-of ? extends TableModel<E>, E) 
       * in the type TableColumn<E,capture#5-of ? extends TableModel<E>> is 
       *not applicable for the arguments (TableModel<E>, E) 
       */
      column.setDisplayValue(this, entry);
    }
}
Thank you in advance, Clemens
  • 1. Re: Recursive type parameters not allowed?
    843793 Newbie
    Currently Being Moderated
    Does this not work:
    class TableModel<E> {
        TableColumn<E, TableModel<E>> column;
        //...
    }
    The problem is in yours, the second type parameter is unknown to the compiler...all the compiler knows is its erasure (TableModel<E>). Therefore you can't use setDisplayValue, because the compiler has no way of knowing if the ? refers to TableModel<E> or one of its subclasses. There is no way it can type safely let you call the method as is.

    It would be similar to having this scenario:
    List<String> list = new ArrayList<String>();
    List<? extends Object> wildcardList = list;
    wildcardList.add(new Object());
    This would be type-unsafe since now you have a List of Strings that contains a non-String.

    Edited by: endasil on 17-Sep-2010 2:13 PM
  • 2. Re: Recursive type parameters not allowed?
    843793 Newbie
    Currently Being Moderated
    Does this not work:
    class TableModel<E> {
    TableColumn<E, TableModel<E>> column;
    //...
    }
    The problem is that TableColumn may also work with subclasses ob TableModel.
    If I try to assign a TableColumn<E, TableModelSubClass<E>> to column which is TableColumn<E, TableModel<E>> I of course get a compiler error.

    I was able to solve the problem by doing a dirty trick - casting TableColumn<E, ? extends TableModel<E>> to TableColumn<E, TableModel<E>> before I call getDisplayValue(), now it works however of course type safety is gone.
    It would be similar to having this scenario:
    List<String> list = new ArrayList<String>();
    List<? extends Object> wildcardList = list;
    wildcardList.add(new Object());
    Wouldn't it be more like this:
    List<String> list = new ArrayList<String>();
    List<? extends String> wildcardList = list; //Assuming String would be non-final
    wildcardList.add("blabla");]
    Shouldn't this work anyway?

    Thanks a lot for your patience and for looking at my problem.

    - Clemens
  • 3. Re: Recursive type parameters not allowed?
    843793 Newbie
    Currently Being Moderated
    linuxhippy wrote:
    Does this not work:
    class TableModel<E> {
    TableColumn<E, TableModel<E>> column;
    //...
    }
    The problem is that TableColumn may also work with subclasses ob TableModel.
    If I try to assign a TableColumn<E, TableModelSubClass<E>> to column which is TableColumn<E, TableModel<E>> I of course get a compiler error.
    As you should, since it is a type error. If you did so and then tried to invoke
    column.setDisplayValue(this, entry);
    from TableModel, this should fail because "this" is not necessarily (in fact quite probably not) a TableModelSubClass.
    I was able to solve the problem by doing a dirty trick - casting TableColumn<E, ? extends TableModel<E>> to TableColumn<E, TableModel<E>> before I call getDisplayValue(), now it works however of course type safety is gone.
    It would be similar to having this scenario:
    List<String> list = new ArrayList<String>();
    List<? extends Object> wildcardList = list;
    wildcardList.add(new Object());
    Wouldn't it be more like this:
    List<String> list = new ArrayList<String>();
    List<? extends String> wildcardList = list; //Assuming String would be non-final
    wildcardList.add("blabla");]
    It would also be similar to this too.
    Shouldn't this work anyway?
    No, it shouldn't. But this is a bad example since String is final; it can't have any subclasses and the compiler would know that. Not all classes are final, just like your TableModel.

    This is a more complete example of your error. Assume this compiled:
    public void addItem(List<? extends Object> list) {
        list.add(new Object());
    }
    Then you could call it like this:
    List<Object> list = new ArrayList<Object>();
    addItem(list);
    Object item = list.get(0);
    This is similar to your example, and would all run fine at runtime. But now consider this:
    List<String> list = new ArrayList<String>();
    addItem(list);
    String item = list.get(0);
    This time, the code above knows that list contains Strings, but your abusive "addItem" method will have added a non-string to the list, resulting in a ClassCastException at the third line.