自类型 - 要求类型绑定中的类型

2022-09-03 16:14:44

我想知道是否有可能限制在接口上声明的方法,以要求在类型绑定内键入类型。实际上,我想提供一种方法来强制转换一种类型,该类型在某种程度上是类型的,因为无法提供真正的类型安全性。

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

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.asSecondIface extends FirstIface


答案 1

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

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

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

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

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

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

我怀疑最接近你的目标的近似值是:

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

答案 2

我能想到的最好的解决方案是:

    interface Base<TYPE extends Base<TYPE, BASE>, BASE extends Base<?, ?>> {

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

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

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

    public static void main(String... args) {
        {
            FirstIface<?> i = new FirstClassA();
            FirstClassA a = i.as(FirstClassA.class);
            FirstClassB b = i.as(FirstClassB.class); // runtime exception
            SecondClassA x = i.as(SecondClassA.class); // compile exception
            SecondClassB y = i.as(SecondClassB.class); // compile exception
        }
        {
            SecondIface<?> i = new SecondClassA();
            FirstClassA a = i.as(FirstClassA.class); // compile exception
            FirstClassB b = i.as(FirstClassB.class); // compile exception
            SecondClassA x = i.as(SecondClassA.class);
            SecondClassB y = i.as(SecondClassB.class); // runtime exception
        }
        new FirstClassA().as(FirstClassB.class); // unfortunately, this compiles fine :-(
    }

推荐