Java自键型方法:无法安全地转换为实际类型

时间:2014-06-09 07:12:43

标签: java generics self-type

考虑以下类,我认为它正确地称为self-typed类:

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;
  }
}

也就是说,扩展此类的实现将具有类似的类型签名:

public class SubExample extends Example<SubExample>

三个get*()方法中的每一个表面上都做同样的事情 - 构造Example的子类,在实例上执行私有方法,并将其作为其子类型返回。但是,只有最后一个示例在没有警告的情况下编译。

即使没有泛型,get1()中的行为也是错误的,请考虑:

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 {}
}

我理解,即使它对我来说似乎是不必要的限制。类似地get3()也是有道理的,尽管我不喜欢这样做。但get2()让我感到困惑。我理解E在技术上是Example<E>的子类型,但是这个通用的界限是否确保所有Example<E>也都是E?如果是这样,为什么这样投射是不安全的?是否有可能在没有警告的情况下从Example<E>投射到E

2 个答案:

答案 0 :(得分:1)

并非所有Example<E>都必须为E s:

public class A extends Example<A> { ... }
public class B extends Example<A> { ... }

Example<A> notAnA = new B();

所以编译器是正确的。

请注意,get3()也可以写成:

public E get3() {
    E obj = construct();
    Example<E> objAsEx = obj;
    objAsEx.specialOp();
    return obj;
}

因此即使没有显式转换,编译器也知道代码是正确的。但是,似乎没有应用这些知识来允许私人成员在没有手持的情况下进行访问。

答案 1 :(得分:0)

通过Manifold's使用@Self更简单,更通用的 Self 类型可以使代码看起来像这样:

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;
  }
}