12

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

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();
}
4

11 回答 11

4

通常如何实施这种情况?

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

getNumberOfLegs()显然应该是一种静态方法(假设在一个完美的世界中,我们没有'使鸡和狗残废,因此getNumberOfLegs不依赖于实例)。

这明显不明显!我们不会为完美世界编程,在现实世界中,四足动物有时有一条、两条或三条(或五条)腿。

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

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

然后在程序开始时初始化这些集合 - 理想情况下从数据库或配置文件中添加动物定义,而无需编写或编译另一行代码。(而且您可以使用更少的类型。)

于 2010-05-30T00:11:09.840 回答
3

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

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

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

另请查看为什么我不能在 Java 接口中定义静态方法?

于 2010-05-29T23:05:34.390 回答
3

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

说静态抽象是无意义的,是不正确的。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()-method 的运行时版本,您可以向其传递Type对象(应该Class用于 Java)。在这种情况下,您必须在运行时进行类型检查,即检查Type-/ Class-object 表示的类型是否继承自Animal,然后检索在属性/注释中传递的值。

用法:

int numberOfLegs1 = Animal.GetNumberOfLegs<Cat>(); 
int numberOfLegs2 = Animal.GetNumberOfLegs(typeof(Cat)); //runtime version
于 2010-05-29T23:12:02.427 回答
2

How do one usually go about implementing this situation?

In Java terms, I'd just declare a constructor in the abstract class which takes a fixed argument. Each subclass is then required to invoke it, else it won't compile.

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);
    }
}

Update: as per your update

EDIT: Here how I might use this. Assume (now this is ridiculous, but anyhow...) that the number of legs of each animal is unique, so I might have something like:

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

This is indeed ridiculous, this requires that all of those concrete animals are known beforehand in the abstract factory method. You would need to update the abstract factory method everytime when a new concrete animal type is added. What's the point of the abstract factory then? You already know everything beforehand? No, just let the abstract factory method take the full qualified classname as identifier or so, so that it can try to load from the classpath (still talking in Java terms).

于 2010-05-29T23:22:18.670 回答
0

You could cludge a runtime check that the method is implemented by having your base class throw an exception in its implementation. It isn't worth much, but maybe better'n nothing...

于 2010-05-30T01:08:08.187 回答
0

Abstract methods make sense if you call them in aid of base class. Note that in your example, you aren't utilizing polimorphism at all. In example should be something like:

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

Anyway PHP implements so called "late static binding" which is probably what you are looking for

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

In C++ similar functionality can be achieved using tamplates and compile-time polymorphism.

于 2010-05-30T01:23:44.707 回答
0

我在这里可以看到的另一种方法是创建一个抽象工厂:这是 C#,您无需创建 Chicken 实例即可知道多条腿。只需调用 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;
    }

}
于 2010-05-31T14:53:17.287 回答
0

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

于 2010-05-29T23:07:57.177 回答
0

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

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

更一般地说,结合抽象和静态没有意义(在Java中),因为抽象意味着多态,而静态调用根本是非多态的,并且在编译时已知的类上工作。

于 2010-05-29T23:52:32.757 回答
0

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

然后使用您的代码,我添加以下内容:

Animal modelAnimal;
int numLegs;

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

numLegs = modelAnimal.getNumberOfLegs();

我会收到错误,因为作为Dog对象的modelAnimal将尝试在Animal类而不是Dog类中调用getNumberOfLegs没有覆盖你知道的静态方法。为了避免这种情况,设计者没有包含抽象的静态方法。

于 2011-06-20T12:03:04.407 回答
0

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

我认为大多数支持将类型(以及类型的实例)分配给变量的编译器/语言也设法通过编译器魔术来支持abstract staticvirtual abstract方法,所以如果它们在您选择的语言中确实有用,那么应该支持它们。

于 2010-05-30T01:39:42.830 回答