«Абстрактный статический» метод - как? - PullRequest
12 голосов
/ 30 мая 2010

Уже есть несколько вопросов о том, почему не существует абстрактного статического метода / поля как такового, но мне интересно, как можно было бы реализовать следующий псевдо-код:

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 ]

4 голосов
/ 30 мая 2010

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

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

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

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

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

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

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

3 голосов
/ 30 мая 2010

Это действительно хороший момент, и иногда 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
3 голосов
/ 30 мая 2010

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

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

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

Пожалуйста, посмотрите также Почему я не могу определить статический метод в интерфейсе Java?

2 голосов
/ 30 мая 2010

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

В терминах 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).

0 голосов
/ 20 июня 2011

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

Затем, используя ваш код, я добавляю это:

Animal modelAnimal;
int numLegs;

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

numLegs = modelAnimal.getNumberOfLegs();

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

0 голосов
/ 31 мая 2010

Еще один подход, который я вижу здесь, - создать абстрактную фабрику: это с # вам не нужно делать экземпляр 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;
    }

}
0 голосов
/ 30 мая 2010
Методы

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

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

0 голосов
/ 30 мая 2010

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

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

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

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

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

0 голосов
/ 30 мая 2010

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

0 голосов
/ 30 мая 2010

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...