Java абстрактный статический Временное решение - PullRequest
24 голосов
/ 16 декабря 2009

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

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

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

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

Ответы [ 14 ]

7 голосов
/ 16 декабря 2009

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

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

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

6 голосов
/ 16 декабря 2009

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

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

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

4 голосов
/ 02 февраля 2012

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

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

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

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

2 голосов
/ 17 декабря 2009

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

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

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

1 голос
/ 17 декабря 2009

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

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 ...

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

1 голос
/ 16 декабря 2009

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

1 голос
/ 16 декабря 2009

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

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

0 голосов
/ 19 апреля 2014

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

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

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

ValueImSearchingFor visf = StaticClass.someArbitraryValue ()

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

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

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

Думайте об этом как об окончательном государственном статусе, который позволяет запустить метод ОДИН РАЗ, чтобы установить его изначально. (Вроде того, что вы можете сделать, когда определяете Enum на самом деле ...)

0 голосов
/ 16 декабря 2009

Я бы создал класс 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";
    }
}
0 голосов
/ 16 декабря 2009

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

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();
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...