Java self-typed methods: cannot safely cast to actual type

被刻印的时光 ゝ 提交于 2019-12-12 04:04:47

问题


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 Es? 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?


回答1:


Not all Example<E>s must be Es:

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.




回答2:


Using Manifold's simpler, more versatile Self type via @Self makes it clear what the code should look like:

import manifold.ext.api.Self;

public abstract class Example {
  /** Constructs an instance of the subclass */
  protected abstract @Self Example construct();

  /** Do a private operation in the base class */
  private void specialOp() {}

  public @Self Example get1() {
    Example obj = construct();
    obj.specialOp();
    return obj;
  }
}


来源:https://stackoverflow.com/questions/24115354/java-self-typed-methods-cannot-safely-cast-to-actual-type

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!