自类型 – 要求在类型约束内部有一个类型

huangapple go评论76阅读模式
英文:

Self-type - requiring a type within a type bound

问题

以下是您提供的内容的翻译部分:

我想知道是否有可能限制在接口上声明的方法,要求在类型边界内的类型。实际上,我希望提供一种在无法提供真正类型安全性的情况下,对类型进行某种程度的类型安全性的方法。

例如,考虑一个通过中间接口继承的基本类型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); // 期望:编译通过,现在:编译错误
FirstClassB b = i.as(FirstClassB.class); // 期望:运行时异常,现在:编译错误

同样的方法也适用于ThirdIface的层次结构,而以下代码应该会导致编译错误:

SecondIface<?> i = new SecondClassA();
SecondClassA a = i.as(SecondClassA.class); // 现在和期望:编译错误
SecondClassB b = i.as(SecondClassB.class); // 现在和期望:编译错误

是否有任何方法可以声明Base.as以满足这个要求?代码是自动生成的,因此在自动生成的接口中提供覆盖也是可能的(类也是自动生成的)。当使用覆盖时,可能会出现SecondIface扩展FirstIface的情况。

英文:

I am wondering if it is possible to restrain a method declared on an interface to require a type within a type bound. In effect, I'd like to offer a method for casting a type that is somewhat type-safe in the absense of being able to provide real type-safety.

As an example, consider a hierarchy with a base type Base which is inherited via an intermediate interface. Typically, one knows about a type being of interface FirstInterface but not what specific class is implementing it. I'd like a method that allows casting to either implementation of an interface without allowing to cast to other implementations of Base as demonstrated in the following example:

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

interface FirstIface&lt;TYPE extends FirstIface&lt;TYPE&gt;&gt; extends Base&lt;TYPE&gt; { }
class FirstClassA implements FirstIface&lt;FirstClassA&gt; { }
class FirstClassB implements FirstIface&lt;FirstClassB&gt; { }

interface SecondIface&lt;TYPE extends SecondIface&lt;TYPE&gt;&gt; extends Base&lt;TYPE&gt; { }
class SecondClassA implements SecondIface&lt;SecondClassA&gt; { }
class SecondClassB implements SecondIface&lt;SecondClassB&gt; { }

interface ThirdIface&lt;TYPE extends ThirdIface&lt;TYPE&gt;&gt; extends FirstIface&lt;TYPE&gt;, SecondIface&lt;TYPE&gt; { }
class ThirdClassA implements ThirdIface&lt;ThirdClassA&gt; { }
class ThirdClassB implements ThirdIface&lt;ThirdClassB&gt; { }

I'd hope to being able to make the following code compile in Java:

FirstIface&lt;?&gt; 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

The same should work for the hierarchy of ThirdIFace, whereas the following code should render a compiler error:

SecondIface&lt;?&gt; 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

Is there any way to declare Base.as to withhold this requirement? The code is auto-generated, so it would also be possible to provide an override in the interfaces which are auto-generated (as are the classes). When overrides are used, a scenario of SecondIface extends FirstIface.

答案1

得分: 1

如果您的目标仅是允许可能成功的调用,其中"可能成功"的意思是我们在尝试将类型为SUPER的实例强制转换为类型为SUB的实例时(在静态情况下),我们知道SUBSUPER的子类型,我不确定是否可能。

即使在解决没有类型变量的问题后,比如中间自身类型(在您的示例中为FirstIface&lt;?&gt;),编译器也会推断SUPER==Object(或SUPER==Base&lt;?&gt;),以满足SUB extends SUPER的要求。

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

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

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

是的。我们只有1)最终实现类型的自身类型变量TYPE,以及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;
    }
}
英文:

If your goal is to only allow calls that are likely to succeed, with likely to succeed meaning that we know (statically) that SUB is a subtype of SUPER when attempting to cast an instance of type SUPER to type SUB, I'm not sure that is possible.

A problem, even after overcoming the issue of not having a type variable for, let's say, the intermediate self type (FirstIface&lt;?&gt; in your example), is that the compiler will infer SUPER==Object (or SUPER==Base&lt;?&gt;) if necessary to satisfy SUB extends SUPER.

> The code is auto-generated, so it would also be possible to provide an override in the interfaces which are auto-generated

I don't think that would help. The goal would be for methods in sub-interfaces to have more restrictive parameter types than what is declared in the supertype, but parameter types are not covariant (covariant parameter types would violate the Liskov substitution principle)

> But since the wildcard could be any subtype of FirstIface, it does not allow for any subtype of FirstIFace

Yeah. We only have 1) the self type variable TYPE which is for the final implementing type, and 2) a type for the Base interface. We don't have a way to write down the type for the intermediate type information that is known at the call site.

I suspect the closest approximation to your goal would be to:

  1. Use a static method for the cast, and
  2. Avoid use of type inference at the call site, since javac will happily infer Object for the supertype to make the code compile.

Of course, this isn't a good solution, since avoiding type inference is impractical. Here is a full example:

public class Hello {
    interface Base&lt;TYPE extends Base&lt;TYPE&gt;&gt; {}

    interface FirstIface&lt;TYPE extends FirstIface&lt;TYPE&gt;&gt; extends Base&lt;TYPE&gt; {}

    static final class FirstClassA implements FirstIface&lt;FirstClassA&gt; { }
    static final class FirstClassB implements FirstIface&lt;FirstClassB&gt; { }

    interface SecondIface&lt;TYPE extends SecondIface&lt;TYPE&gt;&gt; extends Base&lt;TYPE&gt; { }

    static final class SecondClassA implements SecondIface&lt;SecondClassA&gt; { }
    static final class SecondClassB implements SecondIface&lt;SecondClassB&gt; { }

    public static void main(String[] args) {
        FirstIface&lt;?&gt; i = new FirstClassA();

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

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

    static &lt;SUPER, SUB extends SUPER&gt; SUB as(SUPER obj, Class&lt;? extends SUB&gt; c) {
        return (SUB) obj;
    }
}

答案2

得分: 0

    接口 Base<TYPE extends Base<TYPE, BASE>, BASE extends Base<?, ?>> {

        默认 <CAST extends BASE> CAST as(Class<CAST> type) {
            如果 (type.isInstance(this)) {
                返回 type.cast(this);
            } 否则 {
                抛出新的 IllegalArgumentException();
            }
        }
    }

    接口 FirstIface<TYPE extends FirstIface<TYPE>> 扩展 Base<TYPE, FirstIface<?>> {}
    静态类 FirstClassA 实现 FirstIface<FirstClassA> {}
    静态类 FirstClassB 实现 FirstIface<FirstClassB> {}

    接口 SecondIface<TYPE extends SecondIface<TYPE>> 扩展 Base<TYPE, SecondIface<?>> {}
    静态类 SecondClassA 实现 SecondIface<SecondClassA> {}
    静态类 SecondClassB 实现 SecondIface<SecondClassB> {}

    公共静态无返回 main(字符串... 参数) {
        {
            FirstIface<?> i = new FirstClassA();
            FirstClassA a = i.as(FirstClassA.class);
            FirstClassB b = i.as(FirstClassB.class); // 运行时异常
            SecondClassA x = i.as(SecondClassA.class); // 编译异常
            SecondClassB y = i.as(SecondClassB.class); // 编译异常
        }
        {
            SecondIface<?> i = new SecondClassA();
            FirstClassA a = i.as(FirstClassA.class); // 编译异常
            FirstClassB b = i.as(FirstClassB.class); // 编译异常
            SecondClassA x = i.as(SecondClassA.class);
            SecondClassB y = i.as(SecondClassB.class); // 运行时异常
        }
        new FirstClassA().as(FirstClassB.class); // 不幸的是,这个编译通过 :-(
    }
英文:

The best solution I could come up with is:

    interface Base&lt;TYPE extends Base&lt;TYPE, BASE&gt;, BASE extends Base&lt;?, ?&gt;&gt; {

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

    interface FirstIface&lt;TYPE extends FirstIface&lt;TYPE&gt;&gt; extends Base&lt;TYPE, FirstIface&lt;?&gt;&gt; {}
    static class FirstClassA implements FirstIface&lt;FirstClassA&gt; {}
    static class FirstClassB implements FirstIface&lt;FirstClassB&gt; {}

    interface SecondIface&lt;TYPE extends SecondIface&lt;TYPE&gt;&gt; extends Base&lt;TYPE, SecondIface&lt;?&gt;&gt; {}
    static class SecondClassA implements SecondIface&lt;SecondClassA&gt; {}
    static class SecondClassB implements SecondIface&lt;SecondClassB&gt; {}

    public static void main(String... args) {
        {
            FirstIface&lt;?&gt; 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&lt;?&gt; 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 :-(
    }

答案3

得分: 0

如果您在生成`Base`接口时不使用所有泛型的混乱而是了解`FirstIface`:

    interface Base {
        default <CAST extends FirstIface> CAST as(Class<CAST> type) {
            if (type.isInstance(this)) {
                return type.cast(this);
            } else {
                throw new IllegalArgumentException();
            }
        }
    }
    
    interface FirstIface extends Base { }
    class FirstClassA implements FirstIface { }
    class FirstClassB implements FirstIface { }
    
    interface SecondIface extends Base { }
    class SecondClassA implements SecondIface { }
    class SecondClassB implements SecondIface { }
    
    public class Generics {
    
        public static void main(String[] args) {
            FirstIface i = new FirstClassA();
            FirstClassA a = i.as(FirstClassA.class);
            FirstClassB b = i.as(FirstClassB.class); // runtime exception
    
            SecondIface j = new SecondClassA();
            SecondClassA c = j.as(SecondClassA.class); // compiler error
            SecondClassB d = j.as(SecondClassB.class); // compiler error
        }
    }
英文:

If you know the FirstIface when generating the Base interface without all the generics madness:

interface Base {
default &lt;CAST extends FirstIface&gt; CAST as(Class&lt;CAST&gt; type) {
if (type.isInstance(this)) {
return type.cast(this);
} else {
throw new IllegalArgumentException();
}
}
}
interface FirstIface extends Base { }
class FirstClassA implements FirstIface { }
class FirstClassB implements FirstIface { }
interface SecondIface extends Base { }
class SecondClassA implements SecondIface { }
class SecondClassB implements SecondIface { }
public class Generics {
public static void main(String[] args) {
FirstIface i = new FirstClassA();
FirstClassA a = i.as(FirstClassA.class);
FirstClassB b = i.as(FirstClassB.class); // runtime exception
SecondIface j = new SecondClassA();
SecondClassA c = j.as(SecondClassA.class); // compiler error
SecondClassB d = j.as(SecondClassB.class); // compiler error
}
}

huangapple
  • 本文由 发表于 2020年10月8日 17:47:59
  • 转载请务必保留本文链接:https://go.coder-hub.com/64259947.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定