首页 > 解决方案 > 自类型 - 需要类型绑定内的类型

问题描述

我想知道是否可以限制在接口上声明的方法要求类型绑定内的类型。实际上,我想提供一种在无法提供真正类型安全的情况下强制转换类型安全的方法。

例如,考虑具有Base通过中间接口继承的基本类型的层次结构。通常,人们知道一种类型是接口FirstInterface,但不知道实现它的特定类。我想要一种方法,该方法允许转换为接口的任一实现,而不允许转换为其他实现,Base如以下示例所示:

interface Base<TYPE extends Base<TYPE>> {
  default <CAST extends TYPE> CAST as(Class<? extends CAST> type) {
    if (type.isInstance(this)) {
      return type.cast(this);
    } else {
      throw new IllegalArgumentException();
    }
  }
}

interface FirstIface<TYPE extends FirstIface<TYPE>> extends Base<TYPE> { }
class FirstClassA implements FirstIface<FirstClassA> { }
class FirstClassB implements FirstIface<FirstClassB> { }

interface SecondIface<TYPE extends SecondIface<TYPE>> extends Base<TYPE> { }
class SecondClassA implements SecondIface<SecondClassA> { }
class SecondClassB implements SecondIface<SecondClassB> { }

interface ThirdIface<TYPE extends ThirdIface<TYPE>> extends FirstIface<TYPE>, SecondIface<TYPE> { }
class ThirdClassA implements ThirdIface<ThirdClassA> { }
class ThirdClassB implements ThirdIface<ThirdClassB> { }

我希望能够在 Java 中编译以下代码:

FirstIface<?> i = new FirstClassA();
FirstClassA a = i.as(FirstClassA.class); // desired: compiles, now: compiler error
FirstClassB b = i.as(FirstClassB.class); // desired: runtime exception, now: compiler error

这同样适用于 的层次结构ThirdIFace,而以下代码应呈现编译器错误:

SecondIface<?> i = new SecondClassA();
SecondClassA a = i.as(SecondClassA.class); // now and desired: compiler error
SecondClassB b = i.as(SecondClassB.class); // now and desired: compiler error

有什么方法可以声明Base.as不执行此要求吗?代码是自动生成的,因此也可以在自动生成的接口中提供覆盖(就像类一样)。使用覆盖时,会出现SecondIface extends FirstIface.

标签: javagenerics

解决方案


如果您的目标是只允许可能成功的调用,并且可能成功意味着我们(静态地)知道这SUBSUPER尝试将 type 的实例强制转换为 type 时的子SUPER类型SUB,我不确定这是可能的。

一个问题,即使在克服了没有类型变量的问题之后,比如说,中间的 self 类型(FirstIface<?>在你的例子中),编译器会在必要时推断SUPER==Object(或)满足.SUPER==Base<?>SUB extends SUPER

代码是自动生成的,因此也可以在自动生成的接口中提供覆盖

我认为这不会有帮助。目标是让子接口中的方法具有比超类型中声明的更严格的参数类型,但参数类型不是协变的(协变参数类型会违反 Liskov 替换原则)

但由于通配符可以是 FirstIface 的任何子类型,它不允许 FirstIface 的任何子类型

是的。我们只有 1)TYPE用于最终实现类型的 self 类型变量,以及 2)用于Base接口的类型。我们没有办法记下调用站点已知的中间类型信息的类型。

我怀疑最接近您的目标的是:

  1. 对演员表使用静态方法,并且
  2. 避免在调用站点使用类型推断,因为 javac 很乐意推断Object超类型以使代码编译。

当然,这不是一个好的解决方案,因为避免类型推断是不切实际的。这是一个完整的例子:

public class Hello {
    interface Base<TYPE extends Base<TYPE>> {}

    interface FirstIface<TYPE extends FirstIface<TYPE>> extends Base<TYPE> {}

    static final class FirstClassA implements FirstIface<FirstClassA> { }
    static final class FirstClassB implements FirstIface<FirstClassB> { }

    interface SecondIface<TYPE extends SecondIface<TYPE>> extends Base<TYPE> { }

    static final class SecondClassA implements SecondIface<SecondClassA> { }
    static final class SecondClassB implements SecondIface<SecondClassB> { }

    public static void main(String[] args) {
        FirstIface<?> i = new FirstClassA();

        FirstClassA a = Hello.<FirstIface<?>, FirstClassA>as(i, FirstClassA.class); // works
        FirstClassB b = Hello.<FirstIface<?>, FirstClassB>as(i, FirstClassB.class); // runtime error

        SecondClassA c = Hello.<FirstIface<?>, SecondClassA>as(i, SecondClassA.class); // compile error
        SecondClassB d = Hello.<FirstIface<?>, SecondClassB>as(i, SecondClassB.class); // compile error
    }

    static <SUPER, SUB extends SUPER> SUB as(SUPER obj, Class<? extends SUB> c) {
        return (SUB) obj;
    }
}

推荐阅读