Подтвердить что ты не робот

"Абстрактный статический" метод - как?

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

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

Ответ 1

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

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

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

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

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

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

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

Ответ 2

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

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

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

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

Ответ 3

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

Говорить о том, что статический аннотация не имеет смысла, не соответствует действительности. PHP позволяет использовать абстрактные статические методы (см. this), и ваш сценарий показывает, что он может быть полезен в некоторых ситуациях.

Также неверно утверждать, что методы static нельзя переопределить; final методы не могут быть переопределены. В таких языках, как Java и С#, static поставляется с final. Поэтому многие предполагают, что static равно "не переопределяемому".

Говоря о С# (после прочтения ваших комментариев, я предполагаю, что вы "говорите" на С#), вы можете рассмотреть использование дженериков и атрибутов (или генериков и аннотаций в Java):

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

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

   }
}

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

Это позволит вам получить количество ног каждого типа без его создания. Не забудьте указать атрибут [NumberOfLegs(x)]. Вы также должны знать тип во время компиляции (для общей версии метода).

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

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

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

Ответ 4

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

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

Обновить: согласно вашему обновлению

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

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

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

Ответ 5

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

Ответ 6

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

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

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

Ответ 7

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

Ответ 8

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

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

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

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

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

Ответ 9

Методы

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

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

Ответ 10

еще один подход, который я вижу здесь, - сделать абстрактное factory: это С# вам не нужно делать экземпляр цыпленка, чтобы знать несколько ног. просто вызовите метод checken factory

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

}

Ответ 11

На какое-то время допускается принятие абстрактных статических методов.

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

Animal modelAnimal;
int numLegs;

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

numLegs = modelAnimal.getNumberOfLegs();

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