2010-05-29 5 views
12

Есть уже несколько 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, но тогда нужно было бы создать экземпляр класса, чтобы узнать, сколько ног у этого животного - хотя это и не нужно.

Как обычно удается реализовать эту ситуацию?


EDIT: Здесь, как я могу это использовать. Предположим, (теперь это смешно, но так или иначе ...), что число ног каждого животного уникален, так что я мог бы иметь что-то вроде:

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

ответ

3

Ваш псевдокод очень похож на Java, так что я Предположим, что вы используете Java.

«Абстрактный метод требует реализации на один экземпляр. Статические методы относятся к общему классу. Статический метод в абстрактном классе относится к абстрактному классу, а не к потенциальным реализациям. Поэтому не имеет смысла допускать абстрактный статический Кроме того, статические методы не могут быть переопределены, поэтому снова абстрактные статические методы будут аномалией ».

От http://forums.sun.com/thread.jspa?threadID=597378

смотрите, пожалуйста Why can't I define a static method in a Java interface?

+0

-1: Вы не можете сказать это вообще. абстрактный метод не всегда не имеет смысла (см. мой пост). – Simon

+0

Отредактировал свой ответ, чтобы отразить это. –

+0

Мой вопрос должен быть агностиком языка (раньше я сталкивался с аналогичной проблемой на C#). Я вижу, почему в языке нет «абстрактного статического» модификатора, но мне больше интересно узнать, как можно чисто реализовать проблему, о которой я говорил в вопросе. – polyglot

0

Даже если вы могли бы иметь абстрактный статический метод, как бы вы ее использовать? Подумайте, как вы будете использовать его, прежде чем думать о том, как его реализовать, поскольку ваша реализация должна соответствовать потребностям.

+0

@Downvoter: почему бы не рассказать людям, в чем проблема с моим ответом? –

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)]. Вы также должны знать тип во время компиляции (для общей версии метода).

EDIT: Я добавил версию времени выполнения GetNumberOfLegs() -метода, к которому вы можете передать Type объект (должен быть Class для Java). В этом случае вам придется выполнить проверку типа во время выполнения, то есть проверить, наследует ли тип, представленный Type -/Class -объектом, от Animal, а затем извлекает значение, переданное в атрибуте/аннотации.

Использование:

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

Сравнение Java/C# с PHP похоже на сравнение яблок с апельсинами. – BalusC

+2

Это не сравнение ... Я показываю, что абстрактный статический не является бессмысленным. Я указываю общую концепцию ООП. – Simon

+0

извините, но сама по себе абстрактная статика по-прежнему нонсенс. Вам понадобился пользовательский атрибут, чтобы понять смысл этого варианта использования. –

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

Update: в соответствии с вашим обновление

EDIT: Вот как я мог бы использовать это. Предположим (теперь это смешно, но так или иначе ...), что число ног каждое животное является уникальным, так что я мог бы иметь что-то вроде:

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

Это действительно смешно, это требует, чтобы все эти конкретные животные были известны заранее в абстрактном заводском методе. Вам нужно будет обновлять абстрактный заводский метод каждый раз, когда будет добавлен новый тип бетонного животного. Тогда какая точка абстрактной фабрики? Вы уже все знаете заранее? Нет, просто позвольте абстрактному заводскому методу взять полное квалифицированное имя класса как идентификатор или так, чтобы он мог попытаться загрузить из пути к классам (все еще говорящий на Java).

+0

Но 'getNumberOfLegs()' все еще является методом экземпляра, и мне нужно создать экземпляры классов, чтобы узнать, сколько ног у животного ... – polyglot

+0

См. Обновление моего ответа. – BalusC

+0

Метод getModelAnimal() 'не обязательно определен в классе Animal. – polyglot

0

Это интересный вопрос. На мой взгляд, «статические абстрактные» методы редко нужны, и всегда есть хорошие альтернативы.

В используемом варианте, который вы предоставили, например, фабричный метод обращается к конкретным классам животных по названию; для каждого нового класса животных следует добавить новый специальный код. Поэтому кажется, что «абстрактная» квалификация действительно не нужна. Достаточно условно поставлять статический метод getNumberLegs().

И в более общем плане, сочетая абстрактные и статические не имеет смысла (в Java), так как абстрактного подразумевает полиморфизм, в то время как статические вызовы не являются полиморфными вообще, и работа над классами известных во время компиляции.

4

Как обычно обходит , реализуя эту ситуацию?

Обычное решение заключается в том, чтобы сделать рассматриваемый метод методом экземпляра.

getNumberOfLegs(), очевидно, должен быть статический методом (при условии, что в идеальном мире мы «не калечил курицы и собака так getNumberOfLegs является не экземпляр-зависимым).

Это решительно не очевидно! Мы не программируем идеальный мир, и в реальном мире четвероногие животные иногда имеют одну, две или три (или пять) ног.

Если ваша программа нуждается животное определения, а не животное экземпляров, идти вперед и сделать класс для что.

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

Затем инициализировать коллекцию тех, в начале вашей программы - в идеале из базы данных или файла конфигурации, где можно добавлять определения животных без написания или компиляции еще одну строку кода. (И вы можете уйти с гораздо меньшим количеством типов.)

0

Вы можете cludge с выполнения проверки, что метод реализуется при наличии Вашего базового класса сгенерирует исключение в его реализации. Это не стоит многого, но, может быть, ничего лучше ...

0

Абстрактные методы имеют смысл, если вы их называете в помощь базовому классу. Обратите внимание, что в вашем примере вы вообще не используете полиморфизм. В примере должно быть что-то вроде:

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

Во всяком случае PHP реализует так называемый «привязки поздно статический», который, вероятно, что вы ищете

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

В аналогичной функциональности C++ может быть достигнуто с помощью tamplates и полиморфизм времени компиляции.

0

abstract static методы имеют смысл только в языках, в которых переменные могут содержать фактические типы, а не только экземпляры. (Delphi - один из таких языков, C# - нет, и я не думаю, что вы можете это сделать и на Java). Причина в том, что если вы знаете во время компиляции точно, какие классы вы используете (например, в вашем примере), то нет причин для метода быть abstract, вы могли бы просто иметь static методов в каждом классе с одинаковым именем вещи. Единственный способ, которым вы могли бы не знать, какие типы вы используете, - это присвоение типов переменной, так как вы можете передавать их (как и в случае классов), и внезапно все имеет смысл и полезно.

Я думаю, что большинство компиляторов/языков, которые поддерживают присвоение типов (а также экземпляры типов) для переменных, также поддерживают методы abstract static и virtual abstract с помощью магии компилятора, поэтому, если они действительно полезны на вашем языке выбора, тогда они следует поддерживать.

+0

Не только языки с переменными типами. Другое дело, когда такой абстрактный статический метод вызывается из другого метода. Скажем, один из методов Animal вызывает 'Animal.getNumberOfLegs()'. Затем собака может вызвать один из своих методов этот метод. Поскольку 'getNumberOfLegs()' является абстрактным 'Animal.getNumberOfLegs()' внутри тела метода Animal, может быть отправлен на 'Dog.getNumberOfLegs()'. – doc

+0

Этот пример не имеет смысла. Как должен компилятор знать, чтобы отправить статический вызов другому типу на основе подписи нестатического вызова, который вы сейчас используете? Дело не в этом. – Donnie

+0

Я думаю, что дополнительный vtable для статических вызовов должен выполнять эту работу. Во всяком случае, это зависит только от RTTI. PHP делает что-то вроде этого (хотя и не совсем). – doc

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

} 
0

На мгновение предположить «абстрактные статические методы» разрешены.

Затем с помощью кода, я добавляю это:

Animal modelAnimal; 
int numLegs; 

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

numLegs = modelAnimal.getNumberOfLegs(); 

Я получаю ошибку, как modelAnimal который является Dog объект будет пытаться вызвать getNumberOfLegs в животных класс и не Собака класс. Нет переопределения для статических методов, которые вы знаете. Чтобы избежать этой ситуации, дизайнеры не включили абстрактные статические методы.

Смежные вопросы