“抽象静态”方法 - 如何?

时间:2010-05-29 23:03:54

标签: oop static-methods abstract-methods

为什么没有抽象的静态方法/字段,已经有几个SO问题,但我想知道如何实现以下伪代码:

class Animal {
    abstract static int getNumberOfLegs(); // not possible
}

class Chicken inherits Animal {
    static int getNumberOfLegs() { return 2; }


class Dog inherits Animal {
    static int getNumberOfLegs() { return 4; }

问题出在这里:假设我想确保每个继承Animal的类都包含getNumberOfLegs()方法(即几乎就像一个接口,除了我确实希望抽象类实现几个方法这是所有子类共有的,因此纯接口在这里不起作用)。 getNumberOfLegs()显然应该是一种静态的方法(假设在一个完美的世界中我们不会使鸡和狗瘫痪,所以getNumberOfLegs不依赖于实例。

如果没有“抽象静态”方法/字段,可以将方法从Animal类中删除,然后存在某些子类没有该方法的风险。或者可以使getNumberOfLegs成为实例方法,但是必须实例化一个类以找出动物有多少腿 - 即使它没有必要。

通常如何实施这种情况?


编辑:我在这里如何使用它。假设(现在这很荒谬,但无论如何......)每只动物的腿数都是独一无二的,所以我可能有类似的东西:

Animal getModelAnimal(int numberOfLegs) {
   if (numberOfLegs == Chicken.getNumberOfLegs()) return new Chicken();
   else if (numberOfLegs == Dog.getNumberOfLegs()) return new Dog();
}

11 个答案:

答案 0 :(得分:4)

  

通常如何进行   实现这种情况?

通常的解决方案是使有问题的方法成为实例方法。

  显然应该是{p> getNumberOfLegs()   静态方法(假设在   完美的世界,我们不会瘫痪   所以getNumberOfLegs是鸡和狗   不依赖于实例)。

强调显而易见!我们不打算完美的世界,在现实世界中,四足动物有时会有一条,两条或三条(或五条)腿。

如果您的程序需要动物定义而不是动物实例,请继续为 创建一个类。

class AnimalDefinition {
    public string getScientificName();
    public string getCommonName();
    public int    getNumberOfLegs();
    public bool   getIsAmphibious();
    // etc.
}

然后在程序开头初始化那些集合 - 最好是从数据库或配置文件中添加动物定义,而无需编写或编译另一行代码。 (你可以用更少的类型逃脱。)

答案 1 :(得分:3)

您的伪代码看起来很像Java,所以我假设它是您正在使用的Java。

“抽象方法需要按实例实现。静态方法属于整个类。抽象类中的静态方法属于抽象类,而不是潜在的实现。因此,允许抽象静态方法没有任何意义此外,静态方法不能被覆盖,所以抽象的静态方法也是一种异常。“

来自http://forums.sun.com/thread.jspa?threadID=597378

请同时查看Why can't I define a static method in a Java interface?

答案 2 :(得分:3)

这是一个非常好的观点,有时abstract static确实缺失。 但是,由于现在内存不是问题,您肯定可以将getNumberLegs() - 方法实现为实例方法。

说静态抽象是无意义的,不是真的。 PHP允许抽象静态方法(参见this),您的场景显示它在某些情况下可能有用。

也不能说static方法不能被覆盖;无法覆盖 final 方法。在Java和C#等语言中,static附带final。这就是为什么许多人认为static等于“不可覆盖”。

谈论C#(在阅读你的评论后,我认为你“说”C#),你可以考虑使用泛型和属性(或Java中的泛型和注释):

public class Animal
{
   public static int GetNumberOfLegs<T>() where T : Animal
   {
     //Get T's custom attribute "NumberOfLegs" and return its value 
   }

   //EDIT: Added runtime-version of GetNumberOfLegs.
   public static int GetNumberOfLegs(Type t)
   {
     //Get t's custom attribute "NumberOfLegs" and return its value 

   }
}

[NumberOfLegs(4)]
public class Cat { ... };

这将允许您获得每种类型的腿数,而无需实例化它。请记住指出[NumberOfLegs(x)]属性。您还必须在编译时知道类型(对于方法的通用版本)。

编辑:我添加了GetNumberOfLegs()方法的运行时版本,您可以向其传递Type对象(对于Java应该是Class)。在这种情况下,您必须在运行时进行类型检查,即检查Type - / Class - 对象所代表的类型是否继承自Animal,然后检索传入的值属性/注释。

用法:

int numberOfLegs1 = Animal.GetNumberOfLegs<Cat>(); 
int numberOfLegs2 = Animal.GetNumberOfLegs(typeof(Cat)); //runtime version

答案 3 :(得分:2)

  

通常如何实现这种情况?

在Java术语中,我只是在抽象类中声明一个带有固定参数的构造函数。然后需要每个子类调用它,否则它将不会编译。

abstract class Animal {
    private int numberOfLegs;

    public Animal(int numberOfLegs) {
        this.numberOfLegs = numberOfLegs;
    }

    public int getNumberOfLegs() {
        return numberOfLegs;
    }
}

class Chicken extends Animal {
    public Chicken() {
        super(2);
    }
}

class Dog extends Animal {
    public Dog() {
        super(4);
    }
}

更新:根据您的更新

  

编辑:我在这里如何使用它。   假设(现在这很荒谬,但是   无论如何......)腿的数量   每只动物都是独一无二的,所以我可能会有   类似于:

Animal getModelAnimal(int numberOfLegs) {
   if (numberOfLegs == Chicken.getNumberOfLegs()) return new Chicken();
   else if (numberOfLegs == Dog.getNumberOfLegs()) return new Dog();
}

这确实是荒谬的,这要求所有这些具体的动物事先都是抽象的工厂方法。每次添加新的具体动物类型时,您都需要更新抽象工厂方法。那么抽象工厂有什么意义呢?你事先已经知道了吗?不,只是让抽象工厂方法将完全限定的类名作为标识符左右,以便它可以尝试从类路径加载(仍然用Java术语)。

答案 4 :(得分:0)

即使你有一个抽象的静态方法,你会如何使用它?在考虑如何实现它之前,请考虑如何使用它,因为您的实现必须与用途相匹配。

答案 5 :(得分:0)

这是一个有趣的问题。在我看来,很少需要“静态抽象”方法,并且总有很好的替代方法。

例如,在您提供的用例中,工厂方法按名称处理具体的动物类;对于每个新的动物类,应添加新的特定代码。因此,似乎并不真正需要“抽象”资格。提供静态方法getNumberLegs()的惯例就足够了。

更一般地说,结合抽象和静态没有任何意义(在Java中),因为 abstract 意味着多态,而静态调用完全是非多态的,并且可以继续工作编译时已知的类。

答案 6 :(得分:0)

您可以通过让基类在其实现中抛出异常来检查该方法是否已实现,从而检查该方法是否已实现。

您可以通过 运行时 检查该方法是否已实现。它不值得,但也许更好,没什么......

答案 7 :(得分:0)

如果你在基类的帮助下调用它们,抽象方法就有意义了。请注意,在您的示例中,您根本没有使用polimorphism。在示例中应该是这样的:

  (Animal)Dog.getNumberOfLegs() //cast Dog to Animal first

无论如何,PHP实现了所谓的“后期静态绑定”,这可能是你正在寻找的

http://php.net/manual/en/language.oop5.late-static-bindings.php

在C ++中,可以使用tamplates和编译时多态来实现类似的功能。

答案 8 :(得分:0)

abstract static方法只适用于变量可以包含实际类型而不仅仅是实例的语言。 (Delphi是一种这样的语言,c#不是,我认为你也不能用Java做到这一点)。原因是如果你在编译时确切地知道你正在使用哪些类(比如你的例子),那么这个方法没有理由成为abstract,你可能只有{{1}每个类中的方法命名相同的东西。你可能不知道你正在使用什么类型的唯一方法是,如果你可以为变量分配类型,那么你可以传递它们(就像类的实例一样),突然间一切都很有意义并且很有用。

我认为支持为变量分配类型(以及类型实例)的大多数编译器/语言也设法通过编译器魔术来支持staticabstract static方法,所以如果它们实际上是有用的用您选择的语言,他们应该得到支持。

答案 9 :(得分:0)

我在这里看到的另一种方法是制作一个抽象工厂: 这是c# 你不需要做一个鸡的实例来了解一些腿。 只需调用checken工厂方法

abstract class AnimalFactory
{
    public abstract Animal CreateAnimal();
    public abstract int GetLegs();

}
abstract class Animal
{

}
internal class Chicken : Animal
{

}
class CreateChicken : AnimalFactory
{
    public override Animal CreateAnimal()
    {
        return new Chicken();
    }
    public override int GetLegs()
    {
        return 2;
    }

}

答案 10 :(得分:0)

暂时,假设“抽象静态方法”是允许的。

然后使用您的代码,我添加:

Animal modelAnimal;
int numLegs;

modelAnimal = this.getModelAnimal(4); // Got a dog

numLegs = modelAnimal.getNumberOfLegs();

我会收到错误 modelAnimal 这是一个 Dog 对象会尝试在 Animal 中调用 getNumberOfLegs 上课而不是类。 没有覆盖您知道的静态方法。为避免这种情况,设计人员没有包含抽象静态方法。