【问题标题】:Typing for a clone method specified in an interface键入接口中指定的克隆方法
【发布时间】:2009-08-30 11:01:34
【问题描述】:

我正在编写一个需要类来实现 clone() 方法的接口。我对此的天真做法是这样的:

public interface ISolvableGame {
    function clone():ISolvableGame;
    //...
}

其他地方:

public class MyGame implements ISolvableGame {
    public function clone():MyGame {
        // ...
    }
}

我原以为这种签名是合法的,因为MyGame.clone() 返回了一个实现 ISolvableGame 的类的实例,在我看来这满足了接口中的约定。然而,像上面这样的代码会产生一个编译错误,指的是MyGame.clone() 的签名与接口中指定的签名不同。

因此,我的问题是,如果实现的方法必须与接口中的签名完全匹配,我如何制作需要克隆方法的接口?显然,使界面更具体没有任何意义。但是,如果我使实现的方法不那么具体(即,如果我键入 MyGame.clone() 作为返回 ISolvableGame),该克隆方法的其他用户将不再知道他们得到了什么。

我是否需要两个版本的克隆方法,一个键入为ISolvableGame 以满足接口,另一个键入为MyGame 用于在类中使用?还是有更好的方法?


注意: 我正在使用 ActionScript3(一种实现 ECMA4 规范的类 Java 语言)。我将其标记为与语言无关,因为假设 AS3 在处理接口的方式上并不是独一无二的。但如果我上面的示例代码适用于其他语言,那么我的问题可能是我的语言所特有的。


更新:我突然想到检查我的语言的核心库是如何处理这个问题的。例如,有一个IEventDispatcher 接口,它定义了一个方法dispatch():Event - 所以任何调度Event子类 的类都无法实现IEventDispatcher,这最终类似于我的问题.

核心库通过从类EventDispatcher继承 来处理这个问题,该类的存在是为了实现IEventDispatcher。从而获得编译时类型安全,但代价是一开始就淡化了使用接口的意义,因为人们通常更喜欢接口来避免继承带来的问题。

我认为我的选择是:

  • 最终依赖继承,就像核心库所做的那样
  • 实现 Frederik 描述的两种方法,但名称不同
  • 按照 James 的描述牺牲编译时类型安全

答案: 最后,我选择让接口指定cloneToSolvable 方法 - 即,接口指定克隆到接口类型的方法,实现类必须除了他们可能拥有的任何更具体类型的克隆方法之外,还拥有该方法。在我看来,这是最不令人不快的选项。

【问题讨论】:

    标签: actionscript-3 language-agnostic interface methods clone


    【解决方案1】:

    AS3 不允许您进行重载(多个具有相同名称的函数,通过返回类型或参数类型进行区分)只能覆盖(子类可以替换基类实现)。

    例如

    function foo():int {}
    function foo():String {}
    function foo(a:String):void {}
    

    是名为 foo 的函数的重载 - 您不能在 ActionScript 中执行此操作。接口实际上比重载更接近覆盖,因为从某种意义上说,您是“继承”接口。

    // in your specific case you couldn't have these two functions
    // defined within the same scope
    public function clone():MyGame {}
    public function clone():ISolvableGame {}
    

    您尝试做的是混合这两个概念。你想重载你试图覆盖的接口。正如 Fredrik 的帖子所示,即使在支持重载的语言(AS3 不是其中之一)中,您通常也无法同时进行这两种操作。该接口强制您拥有一个具有确切签名的函数:clone():ISolvableGame,并且您不能用额外的 clone():MyGame 重载它

    如果您正在寻找类型安全,您无法在编译时获得它,但如果您在调用者处检查返回类型,您将能够在运行时获得它。

    例如

    // this will throw if clone does not return an
    // object that is cast-able to MyGame
    var game:MyGame = MyGame(someObj.clone());
    

    我也希望能够在 MyGame 内执行合同,但我不知道如何。

    【讨论】:

    • 感谢您的评论。请查看我的更新,如果您同意我的总结,请告诉我。
    【解决方案2】:

    我没有使用 ActionScript 的经验,所以这可能是一个可行的解决方案,也可能不是,但在 C# 中我通常这样做:

    public class MyGame : ISolvableGame {
        public MyGame clone(){
            // ...
        }
    
        ISolvableGame ISolvableGame.clone() {
            return this.clone();
        }
    }
    

    换句话说,我创建了一个“类型化”克隆方法(它没有实现接口,因为它的返回类型不同)。然后我对接口方法进行了显式实现,它将调用类型化的克隆方法并返回结果。这是一个合法的举动,因为MyGame 可以转换为ISolvableGame

    【讨论】:

    • 好吧,如果这两个方法有不同的名称,我绝对可以这样做,但不幸的是,AS3 不允许这种重载......所以任何调用者都必须知道是否调用 clone() 或cloneToISolvable(),这有点令人不快。不过,这可能是唯一的方法..
    【解决方案3】:

    继承的克隆方法总是有超类作为它们的返回类型。例如,所有事件类都覆盖了flash.events.Event类的clone方法,该方法返回一个Event对象,并实现它以返回各自的派生类对象。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-07
      • 1970-01-01
      • 2023-03-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多