代码之家  ›  专栏  ›  技术社区  ›  Iain

人们如何绕过子类init方法,这些方法需要不同数量的参数来支持他们的超级类?

  •  3
  • Iain  · 技术社区  · 14 年前

    假设我有一个带有基本实体类的游戏,带有一个不带参数的init方法。现在我有了一个向导类,但是我想传递两个参数,比如速度和力量。在AS3中(我相信Java和C.*)不允许这样做——这是一个“不兼容的重写”,因为方法签名不匹配。现在我可以改为创建一个“initwizard”方法,但是我有一个问题,即每个类的init方法可能具有不同的名称。

    我需要在AS3、Java或C语言中工作的解决方案。

    2 回复  |  直到 14 年前
        1
  •  1
  •   ewernli    14 年前

    我不确定我理解你的问题。但是下面的Java编译:

    public class A {
        public void init(int a) {}
    }
    
    public class B extends A {
    
        public void init( int a, int b )
        {
            super.init( a );
        }
    }
    

    尽管我同意 B#init(int,int) 不是的重写 A#init(int) B#init(int) 仍然可以调用。你不能真正隐藏 B.I.init(int) ,最多,您可以在 B 如果被调用,就让它抛出一个异常。

    不过,这是个大问题吗?方法签名只捕获如何使用类的契约的一部分。如果您想对代码进行更多的屏蔽,可以使用工厂方法来创建 A 并使 init 受保护的。

    public class A {
        protected void init(int a) {}
    
        static public A create( int a)
        {
            A o = new A();
            o.init( a );
            return o;
        }
    }
    
    public class B extends A {
    
        protected void init( int a, int b )
        {
            super.init( a );
        }
    
        static public B create( int a, int b)
        {
            B o = new B();
            o.init( a, b );
            return o;
        }
    }
    
        2
  •  6
  •   back2dos    14 年前

    AS3不支持方法重载,但坦率地说,方法重载只是一种语法甜头,通常只会引入歧义和不一致性。

    在Java中,一个方法是由它的名字和它的签名来确定的,在AS3中,只有它的名字,但是语义上,只有很少的差别。

    protected void init( int a )
    protected void init( int a, int b )
    

    protected void init1( int a )
    protected void init2( int a, int b )
    

    在AS3中,每个名称只能有一个方法,每个类只能有一个构造函数。但是,构造函数不需要具有兼容的签名。在AS3中,您可以这样解决问题:

    package {
     class A {
      private var a:int;
      public function A(a:int) {
       this.a = a;
      }
      // ... probably some meaningful methods here :)
     }
    }
    package {
     class B extends A {
      private var b:int;
      public function B(a:int, b:int) {
       super(a);
       this.b = b;
      }
      // ... probably some other meaningful methods here as well :D
     }
    }
    

    您还可以避免EWNLLI提出的Java解决方案中的问题。

    编辑: 刚刚看到,您坚持不使用构造函数,而是使用具有不同签名的init方法。为什么?这违反了 Liskov substitution principle .

    编辑2: 我想你有两种选择:

    1. 初始值设定项的名称不同(不是最好的主意,也因为您的子类将公开初始值设定项,只部分工作(不好,请参见上面提到的lsp))。
    2. 按照哲学生活,任何类都必须是抽象的或最终的。示例(假设为空间游戏):

    抽象基类,多个具体子类:

    class ShipBase {//in Java you might wanna put the 'abstract' keyword just infront
     //... implementation of some sort
     //... maybe a protected intializer to call by subclasses
     //... no public initializer, since class is abstract
    }
    final class Fighter extends ShipBase {
     //... public initializer specific to Fighter
     //... other custom behaviour
    }
    final class Bomber extends ShipBase {
     //... I guess, this is obvious
    }
    

    现在你可以问自己:如果我想怎么办? Fighter 成为一个基础阶级 EliteFighter2000 ,但也是一个可实例化的类。简单。不一定非得这样。

    class FighterBase extends ShipBase {
        //... implementation of whatever kind of things fighters have in common
        //... a protected intializer to call by subclasses    
    }
    final class Fighter extends FighterBase {
        //... public initializer possibly just forwarding to protected initializer
    }
    final class EliteFighter2000 extends FighterBase {
        //... here goes all the 'elite' stuff
    }
    

    这也更加灵活。你现在可以改变简单的 战士 不影响 精英2000 或者你可以决定通过修改来改变所有战士的共同行为。 FighterBase . 继承通常是一件危险的事情,经常被误解和误用。
    坚持这一原则通常有助于避免由于缺乏关注点的分离而引入的微妙的、但往往影响深远的耦合继承。在这种情况下,战斗机的共同点是 战斗机基地 以及任何特定于简单的 Fighters 就在它所属的地方。

    尔兹
    后台操作系统