Consider the following class, which I believe is correctly called a self-typed class:
public abstract class Example<E extends Example<E>> {
/** Constructs an instance of the subclass */
protected abstract E construct();
/** Do a private operation in the base class */
private void specialOp() {}
public E get1() {
E obj = construct();
// Error: The method specialOp() from the type Example<E> is not visible
obj.specialOp();
return obj;
}
public E get2() {
Example<E> obj = construct();
obj.specialOp();
// Warning: Type safety: Unchecked cast from Example<E> to E
return (E)obj;
}
public E get3() {
E obj = construct();
((Example<E>)obj).specialOp();
return obj;
}
}
That is to say, implementations extending this class would have a type signature like so:
public class SubExample extends Example<SubExample>
Each of the three get*()
methods ostensibly do the same thing - construct a subclass of Example
, execute a private method on the instance, and return it as its subtype. However only the last example compiles without warnings.
The behavior in get1()
is an error even without generics, consider:
public class Example {
private void specialOp() {};
public void get(SubExample e) {
// Error: The method specialOp() from the type Example is not visible
e.specialOp();
}
public static class SubExample extends Example {}
}
Which I understand, even if it seems unnecessarily restrictive to me. And similarly get3()
makes sense, though I dislike needing to cast like that. But get2()
confuses me. I understand E
is technically a subtype of Example<E>
, but don't the bounds of this generic ensure that all Example<E>
s are also E
s? If so, why is it not safe to cast like this? Is it ever possible to cast from Example<E>
to E
without a warning?
Not all Example<E>
s must be E
s:
public class A extends Example<A> { ... }
public class B extends Example<A> { ... }
Example<A> notAnA = new B();
So the compiler is correct.
Note that get3()
can also be written:
public E get3() {
E obj = construct();
Example<E> objAsEx = obj;
objAsEx.specialOp();
return obj;
}
So the compiler knows that code is correct, even without an explicit cast. It doesn't seem to apply this knowledge to allow the private member access without having it's hand held, though.