所以我创建了这个简单的界面:
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
但是我不明白为什么会出现问题,因为AwareNode
是Node
的子类。
有什么方法可以让我的代码工作,同时保持代码的完整性?
答案 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实例,你将节省单个实例的内存占用(因为静态方法因此静态只存在一个副本,但实例方法将被复制到每个实例)。缺点是您将无法使用界面(实际上可能是一种改进......取决于您的个人喜好)。