0

我正在编写一个需要类来实现 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方法 - 即,接口指定一个克隆到接口类型的方法,并且实现类必须具有该方法以及它们的任何更具体类型的克隆方法可能有。在我看来,这似乎是最不令人不快的选择。

4

3 回答 3

1

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

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

    ISolvableGame ISolvableGame.clone() {
        return this.clone();
    }
}

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

于 2009-08-30T11:10:06.897 回答
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但我不知道该怎么做。

于 2009-08-30T18:01:24.130 回答
0

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

于 2009-09-01T07:20:36.473 回答