【问题标题】:Java self-typed methods: cannot safely cast to actual typeJava 自类型方法:不能安全地转换为实际类型
【发布时间】:2014-06-09 07:12:43
【问题描述】:

考虑以下类,我认为它被正确称为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&lt;E&gt; 的子类型,但是这个泛型的边界不能确保所有Example&lt;E&gt;s 也是Es 吗?如果是这样,为什么这样投射不安全?是否可以在没有警告的情况下从Example&lt;E&gt; 转换为E

【问题讨论】:

  • 是否可以将List&lt;Integer&gt; 转换为Integer
  • 使 specialOp 方法受保护而不是私有。
  • @alfasin since E extends Example&lt;E&gt; 是的,可能Example&lt;E&gt; 转换为E。自类型签名旨在保证所有E,但我不确定这是否属实。
  • @Henry 谢谢,是的,这将解决编译问题。但这不是我的问题。
  • 我接受的答案提供了一个反例,并证明引用“必须相同”是不正确的。

标签: java generics self-type


【解决方案1】:

并非所有Example&lt;E&gt;s 都必须是Es:

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

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

【讨论】:

  • 好了!谢谢,事后看来,这是一个非常明显的反例。我知道 Java 对自类型泛型的支持充其量是有限的,但是是否有可能构造一个更严格的泛型类型来真正强制自类型化?
  • @dimo414:不,这是不可能的。这就是为什么像class Example&lt;E extends Example&lt;E&gt;&gt; 这样的声明对于人们使用它的目的毫无用处; class Example&lt;E&gt; 也可以。
  • @newacct 这不太对 - 前者仍然保证它是 Example&lt;E&gt; 的子类 - 否则我的 get3() 将无法编译。
  • @dimo414:但这是一个人为的例子,尝试使用这种绑定的人很少遇到
【解决方案2】:

通过@Self 使用Manifold's 更简单、更通用的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;
  }
}

【讨论】:

    猜你喜欢
    • 2018-11-19
    • 1970-01-01
    • 2020-04-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-02
    • 1970-01-01
    • 2013-01-02
    相关资源
    最近更新 更多