tags:

views:

198

answers:

2

Imagine a generic class MySet which maintains a parent MySet instance and a child MySet instance. The idea is that the parent should be able to hold a superset of T and the child a subset. So given the following sample, consider the following problem:

class MySet<T> {

  MySet<? extends T> child; 

  void doStuff (Collection<? extends T> args) {
    child.doStuff(this, args);
  }

}

EDIT: fixed question and sample code to reflect the real problem

Now, the child generic <T> may be more restrictive than the parent's <T>, so the parent must pass in a Collection<X> where <X> conforms to the child's <T>. Keep in mind that this parent->child chain could extend to be arbitrarily long. Is there any way to arrange the generics so that parent.doStuff(...) will compile, ie, so that it can only be called with the arguments of it's most restrictive child?

This would mean that the java compiler would pass up generic information all the way up the parent->child chain to determine what the allowable arguments to doStuff could be, and I don't know if it has that capability.

Is the only solution to ensure children cannot be more restrictive than their parents using generics (ie, MySet<T> child; rather than MySet<? extends T>) and have children be more restrictive than their parents elsewhere in the code?

A: 

The code as presented doesn't seem to be attempting to make any sense.

Consider

 MySet<String> c = ...;
 MySet<Object> p = new MySet<Object>(c);
 Collections<Integer> ints = ...;
 p.doStuff(ints);

That is going to call c.doStuff(???, ints). ints is of type Collection<Integer>, but the callee requires doStuff(Collection<? extends String>).

Tom Hawtin - tackline
+1  A: 

I can give a negative answer to part of that question right away:

Is there any way to arrange the generics so that parent.doStuff(...) will compile, ie, so that it can only be called with the arguments of it's most restrictive child?

This would mean that the java compiler would pass up generic information all the way up the parent->child chain to determine what the allowable arguments to doStuff could be, and I don't know if it has that capability.

The simple answer is no, because the actual extend (and type requirements) of that chain are only known at run time, and by then any information about generics has been lost through erasure.

To push it even further, if you had a checked type instead of vanilla generics, with a method that could pass up in the chain the (most restrictive) actual type accepted, you could do a check at runtime, and raise a runtime error, but it will never be a compile error.

So no, unless the actual final type is known (and specified, maybe by a second type arg) in advance, the compiler is not going to be able to help you there.

What you can (and should do imo) is keep with what you just wrote (which should compile just fine), and pass it an argument that can be unsafe. Whether you type-check it yourself, or let the JVM raise a ClassCastException is needed, remains a matter of choice.

Varkhan
Thanks! In retrospect it should have been obvious that this couldn't be handled at compile time, but you've certainly made it obvious for me :)
Soonil