为什么没有抽象的静态方法/字段,已经有几个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();
}
答案 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}每个类中的方法命名相同的东西。你可能不知道你正在使用什么类型的唯一方法是,如果你可以为变量分配类型,那么你可以传递它们(就像类的实例一样),突然间一切都很有意义并且很有用。
我认为支持为变量分配类型(以及类型实例)的大多数编译器/语言也设法通过编译器魔术来支持static
和abstract 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 上课而不是狗类。 没有覆盖您知道的静态方法。为避免这种情况,设计人员没有包含抽象静态方法。