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

Java абстрактное статическое обходное решение

Я понимаю, что ни абстрактный класс, ни интерфейс не могут содержать метод абстрактный и статический из-за проблем с неоднозначностью, но есть ли способ обхода?

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

РЕДАКТИРОВАТЬ 1: Контекст этой проблемы состоит в том, что у меня есть куча классов, назовите их Stick, Ball и Toy на данный момент, у которых есть куча записей в базе данных. Я хочу создать суперкласс/интерфейс под названием Fetchable, для которого требуется статический метод getFetchables() в каждом из нижележащих классов. Причина, по которой методы в Stick, Ball и Toy должны быть статичными, заключается в том, что они будут разговаривать с базой данных для извлечения всех записей в базе данных для каждого класса.

РЕДАКТИРОВАТЬ 2: Тем, кто говорит, что вы не можете делать это на любом языке, это не так. Вы, безусловно, можете сделать это в Ruby, где методы класса наследуются. Это не случай, когда кто-то не получает OO, это случай отсутствия функциональности на языке Java. Вы можете попытаться утверждать, что вам никогда не нужно наследовать статические (класс) методы, но это совершенно неправильно, и я проигнорирую любые ответы, которые делают такие моменты.

4b9b3361

Ответ 1

У вас есть несколько вариантов:

  • Используйте отражение, чтобы увидеть, существует ли метод, а затем вызвать его.
  • Создайте аннотацию для статического метода с именем вроде @GetAllWidgetsMethod.

  • Как говорили другие, старайтесь не использовать статический метод.

Ответ 2

Есть много ответов о том, что "это не имеет смысла...", но я действительно встретил аналогичную проблему только вчера.

Я хотел использовать наследование с моими модульными тестами. У меня есть API и несколько его реализаций. Поэтому мне нужно всего лишь 1 набор модульных тестов для всех реализаций, но с разными статическими методами setUp.

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

Ответ 3

Я тоже занимаюсь этой проблемой. Для тех, кто настаивает на том, что это "не имеет смысла", я бы предложил вам подумать за пределами этой семантической коробки на мгновение. Программа, с которой я работаю, по своей сути является отражением.

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

Все работает, в первую очередь. Я построил все это. Единственный улов заключается в том, что модуль может быть скомпилирован в .class без проверки времени компиляции, чтобы увидеть, существует ли идентифицирующая статическая функция вообще, в результате чего появляется врожденный бесполезный класс. Без идентификатора и его включенной информации для безопасности модуль не загружен.

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

Ответ 4

Статические методы относятся ко всему классу объекта, а не к отдельным экземплярам. Разрешение переопределения статического метода нарушает это изречение.

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

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

Ответ 5

Есть ли способ сделать это в Java?

Я не думаю, что есть способ сделать это на любом языке. Там нет никакого смысла, поскольку статические методы принадлежат классу и не могут быть названы полиморфно. И включение полиморфных вызовов является единственной причиной существования интерфейсов и абстрактных классов.

Ответ 6

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

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

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

В любом случае я предоставлю сложный способ выразить ваше ограничение, но НЕ НРАВИТСЯ. люди действительно увлекаются тем, что делают все, что можно проверить во время компиляции, ценой нечитаемого кода.

interface WidgetEnumerator
{
    List getAllWidgets();
}

public class Abs<T extends WidgetEnumerator>
{
    static List getAllWidgets(Class<? extends Abs> clazz){ ... }
}

public class Sub extends Abs<SubWidgetEnumerator>
{
}

public class SubWidgetEnumerator implements WidgetEnumerator
{
    public List getAllWidgets() { ... }
}

Как это работает: для любого подкласса Abs он вынужден предоставить реализацию WidgetEnumerator. автор подкласса не может забыть об этом. Теперь вызов Abs.getAllWidgets(Sub.class) содержит достаточную информацию для решения этой реализации, т.е. SubWidgetEnumerator. Это делается путем отражения, но это безопасный тип, нет строковых литералов.

Ответ 7

Думаю, я смогу дать вам лучший ответ после просмотра ваших изменений - лучший выбор - это, вероятно, шаблон factory. (Не прелесть, но лучше, чем синглтон).

abstract class Widget
    public static Widget[] getAllWidgetsOfType(Class widgetType) {
        if(widgetType instanceof ...)
    }
class Ball extends Widget
class Stick extends Widget
class Toy extends Widget

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

Большая проблема в том, что он требует редактирования базового класса всякий раз, когда вы добавляете новый класс данного типа. Это невозможно обойти без размышлений. Если вы хотите использовать отражение, то вы можете реализовать его таким образом (Psuedocode, я не собираюсь искать точный синтаксис для отражения, но это не намного сложнее, чем это):

public static Widget[] getAllWidgetsOfType(Class widgetType) {
    Method staticMethod=widgetType.getStaticMethod("getAllInstances");
    return staticMethod.invoke();
}

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

Вы также можете сделать это методом экземпляра вместо статического. Это не обязательно, но вы могли бы прототип метода (абстрактного) в Widget.

Опять же, все это не нужно и небрежно по сравнению с Hibernate...

Изменить: Если вы передали живой "пустой" экземпляр шара, палки или игрушек вместо этого объекта "Class", вы могли бы просто вызвать унаследованный метод и вообще не использовать отражение. Это также сработает, но вы должны расширить определение виджета, чтобы включить "пустой" экземпляр, используемый в качестве ключа.

Ответ 8

Нет. Вы не можете этого сделать. Если вы готовы пойти на компромисс и сделать метод нестационарным или обеспечить реализацию статического метода в абстрактном классе, вы сможете закодировать его в Java.

Ответ 9

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

Пропустите экземпляры реализации объекта контекста.

Ответ 10

статические методы не могут быть абстрактными, потому что они не являются виртуальными. Поэтому в любом месте, которое их вызывает, должен быть конкретный тип с реализацией. Если вы хотите, чтобы все реализации интерфейса имели определенный статический метод, то это предполагает, что требуется unit test.

abstract class A
{
    public static void foo()
    {
        java.lang.System.out.println("A::foo");
    }

    public void bar()
    {

        java.lang.System.out.println("A::bar");
    }
}

class B extends A
{
    public static void foo()
    {
        java.lang.System.out.println("B::foo");
    }

    public void bar()
    {

        java.lang.System.out.println("B::bar");
    }
}

public class Main
{
    public static void main(String[] args)
    {
        B b = new B();
        b.foo();
        b.bar();

        A a = b;
        a.foo();
        a.bar();
    }
}

Ответ 11

Для чего я знаю точно, что вы пытаетесь сделать.

Я нашел эту статью, ища причины, по которым я тоже не могу этого сделать.

В моем случае у меня есть СТО классов, которые наследуются от центральной базовой базы, и я хочу просто получить ссылку вроде этого:

ValueImSearchingFor visf = StaticClass.someArbitraryValue()

Я НЕ хочу писать/поддерживать someArbitraryValue() для каждого из сотен унаследованных классов - я просто хочу написать логику один раз и рассчитать уникальное значение Class-Sepcific для каждого будущего написанного класс БЕЗ касания базового класса.

Да, я полностью получаю OO. Я писал Java примерно столько, сколько он был доступен.

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

Подумайте об этом как о ПУБЛИЧНОМ СТАТИЧЕСКОМ ОКОНЧАНИИ, который позволяет вам запустить метод ONCE, чтобы установить его на начальном этапе. (Вроде как вы можете сделать, когда вы определяете Enum на самом деле...)

Ответ 12

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

В абстрактном супер классе я буду иметь статический метод getAllWidgets (тип класса). В нем я проверю класс, который вы его передали, и сделайте правильную выборку на основе этого. Как правило, мне нравится избегать прохождения классов и с помощью переключателей на таких вещах, но я сделаю исключение здесь.

Ответ 13

Я бы сделал класс WidgetCollection с абстрактным внутренним классом Widget.

Вы можете расширить класс WidgetCollection.Widget для каждого типа виджета.

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

Пример (не скомпилирован или не протестирован):

class WidgetCollection<W extends Widget> {
    Set<W> widgets = new HashSet<W>();

    Set<W> getAll() {
        return widgets;
    }

    abstract class Widget {

       Widget() {
           widgets.add(this);
       }

       abstract String getName();
    }

    public static void main(String[] args) {
         WidgetCollection<AWidget> aWidgets = new WidgetCollection<AWidget>();
         a.new AWidget();

         Set<AWidget> widgets = aWidgets.getAll();
    }
}

class AWidget extends Widget {
    String getName() {
        return "AWidget";
    }
}