3 Replies Latest reply: Sep 17, 2010 2:47 PM by 843793 RSS

    Recursive type parameters not allowed?

    843793
      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
          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
            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
              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.