如何创建一个子类,为同一个函数名称接受不同的参数?

时间:2011-11-01 00:42:47

标签: actionscript-3 class interface subclass

所以我创建了这个简单的界面:

package{
    public interface GraphADT{
        function addNode(newNode:Node):Boolean;     
    }
}

我还创建了一个简单的类Graph:

package{

    public class Graph implements GraphADT{

        protected var nodes:LinkedList;

        public function Graph(){
            nodes = new LinkedList();
        }

        public function addNode (newNode:Node):Boolean{
            return nodes.add(newNode);
        }
}

最后但并非最不重要的是我创建了另一个简单的类AdjacancyListGraph:

package{
    public class AdjacancyListGraph extends Graph{

        public function AdjacancyListGraph(){
            super();
        }

        override public function addNode(newNode:AwareNode):Boolean{
            return nodes.add(newNode);
        }
}

在此设置此设置会给我错误,即:

1144: Interface method addNode in namespace GraphADT is implemented with an incompatible signature in class AdjacancyListGraph.

仔细观察后,AS3显然不喜欢来自Graph的不同Graph类newNode:Node的不同参数类型,以及来自AdjacancyListGraph的newNode:AwareNode

但是我不明白为什么会出现问题,因为AwareNodeNode的子类。

有什么方法可以让我的代码工作,同时保持代码的完整性?

3 个答案:

答案 0 :(得分:3)

简单回答

如果你真的没有,真的需要你的'addNode()'函数只接受AwareNode,你只需将参数类型更改为Node。由于AwareNode扩展了Node,因此您可以毫无问题地传入AwareNode。您可以在函数体中检查类型的正确性:

subclass... {
    override public function addNode (node:Node ) : Boolean {
        if (node is AwareNode) return nodes.add(node);
        return false;
    }
}

更长的答案

我同意@ 32bitkid您的错误,因为您的界面中为addNode()定义的参数类型与您的子类中的类型不同。

然而,手头的主要问题是ActionScript通常不允许函数重载(具有多个同名方法,但具有不同的参数或返回值),因为每个函数都被视为泛型类成员 - 与变量相同的方式。你可以调用这样的函数:

myClass.addNode (node);

但您也可以这样称呼它:

myClass["addNode"](node);

每个成员都按名称存储 - 您始终可以使用该名称来访问它。不幸的是,这意味着你只允许在一个类中使用每个函数名一次,无论它采用哪种类型的参数 - 没有任何代价:你在一方面获得灵活性,在另一方面失去一些安慰。

因此,您只能覆盖具有完全相同签名的方法 - 这是一种让您坚持在编写基类时所坚持的方法。虽然你明显可以说这是一个坏主意,并且使用重载或允许子类中的不同签名更有意义,AS处理函数的方式有一些优点,这最终将帮助您解决问题:可以使用类型检查功能,甚至可以将其作为参数传递!

考虑一下:

class... {

    protected function check (node:Node) : Boolean {
        return node is Node;
    }    

    public function addNode (node:Node) : Boolean {
        if (check(node)) return nodes.add(node);
        return false;
    }
}

在此示例中,您可以覆盖check(node:Node):

subclass... {
    override protected function check (node:Node) : Boolean {
        return node is AwareNode;
    } 
}

并在不破坏接口契约的情况下实现所需的完全相同的效果 - 除非在您的示例中,如果传入错误的类型,编译器将抛出错误,而在此版本中,错误只会在runtime(false返回值)。

你也可以让它变得更有活力:

class... {
    public function addNode (node:Node, check : Function ) : Boolean {
        if (check(node)) return nodes.add(node);
        return false;
    }
}

请注意,此addNode函数接受一个Function作为参数,并且我们调用该函数而不是类方法:

var f:Function = function (node:Node) : Boolean {
    return node is AwareNode;
}

addNode (node, f);

这将使您可以非常灵活地实现 - 您甚至可以在匿名函数中进行合理性检查,例如验证节点的内容。除非你要添加其他功能,否则你甚至不必扩展你的类。

拥有一个接口还允许您创建不从原始基类继承的实现 - 您可以编写一个完整的不同类层次结构,它只需要实现该接口,并且您之前的所有代码都将保持有效。

答案 1 :(得分:2)

我想这个问题真的是这样的:你想要完成什么?

至于您收到错误的原因,请考虑以下事项:

public class AnotherNode extends Node { }

然后:

var alGraph:AdjacancyListGraph = new AdjacancyListGraph();

alGraph.addNode(new AnotherNode());  
// Wont work. AnotherNode isn't compatable with the signature
// for addNode(node:AwareNode)

// but what about the contract? 

var igraphADT:GraphADT = GraphADT(alGraph);
igraphADT.addNode(new AnotherNode()); // WTF?

根据界面,这应该没问题。但是你的实现说不然,你的实现说它只会接受AwareNode。有明显的不匹配。如果您要拥有一个接口,一个您的对象应该遵循的合同,那么您也可以遵循它。否则,首先是界面的重点。

如果您尝试这样做,我认为架构搞砸了。即使语言支持它,我也会说它是一个“坏主意™”

答案 2 :(得分:1)

有一种更简单的方法,然后在上面提出,但不太安全:

public class Parent {
public function get foo():Function { return this._foo; }
protected var _foo:Function = function(node:Node):void { ... }}

public class Child extends Parent {
public function Child() {
super();
this._foo = function(node:AnotherNode):void { ... }}}

当然_foo不需要声明到位,所使用的语法仅用于简短和演示目的。 您将失去编译器检查类型的能力,但运行时类型匹配仍将适用。

另一种方法 - 不要在他们专门的类中声明方法,而是将它们设置为静态,然后你不会自动继承它们:

public class Parent {
public static function foo(parent:Parent, node:Node):Function { ... }}

public class Child extends Parent {
public static function foo(parent:Child, node:Node):Function { ... }}

请注意,在第二种情况下,可以在静态方法中访问受保护的字段,因此您可以实现某些封装。此外,如果你有很多Parent或Child实例,你将节省单个实例的内存占用(因为静态方法因此静态只存在一个副本,但实例方法将被复制到每个实例)。缺点是您将无法使用界面(实际上可能是一种改进......取决于您的个人喜好)。