Условно удалить методы Java во время компиляции

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

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

public class Preprocessor
{
  public static final boolean FULLACCESS = false;
}

Затем я использую это следующим образом:

public ClassName getClassName()
{
    if(Preprocessor.FULLACCESS)
    {
        return this;
    }
    else
    {
        return this.DeepCopy();
    }
}

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

public ClassName()
{
    // do things
}

if(FULLACCESS)
{
public ClassName(ClassName thing)
{
    // copy contents from thing to the object being created
}
}

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

Благодарим вас за любую помощь.

Изменить:

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

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

Edit2:.

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

Ответ 1

Этот ответ основан частично на комментариях, которые вы оставили на вопрос, и на ответе Марка.

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

public interface A
{
    void f();
}

A выше - ваш общий API. Теперь вы хотите иметь специальные специальные методы для тестирования A или отладить его или манипулировать им или что-то еще...

public interface B extends A
{
    void specialAccess();
}

Кроме того, Java теперь поддерживает реализацию методов по умолчанию для интерфейсов, которые могут быть полезны для вас в зависимости от того, как вы реализуете свой API. Они принимают следующий вид...

public interface A
{
    List getList();

    // this is still only an interface, but you have a default impl. here
    default void add(Object o)
    {
        getList().add(o);
    }
}

Вы можете узнать больше о методах по умолчанию на Страница Oracle об этом здесь.

В вашем API ваше общее распространение может включать в себя A и полностью опустить B и опустить любые реализации, предлагающие специальный доступ; то вы можете включить B и специальные реализации для специальной версии доступа API, о которой вы говорили. Это позволило бы простыми старыми объектами Java, ничем не отличающимися от кода, кроме дополнительного интерфейса и, возможно, дополнительной реализации. Пользовательская часть будет только в вашей упаковке библиотеки. Если вы хотите передать кому-то "неспециализированную" версию с низким доступом, передайте им банку, которая не включает B и не включает в себя возможные BImplementation, возможно, с помощью отдельной сборки script.

Я использую Netbeans для работы с Java, и мне нравится использовать сценарии сборки по умолчанию, которые он создает автоматически. Поэтому, если бы я делал это, и я делал это в Netbeans, я бы, вероятно, создал два проекта: один для базового API и один для API специального доступа, и я бы сделал особый доступ зависимым от базового проекта. Это оставило бы меня с двумя баночками вместо одного, но я был бы в порядке с этим; если бы две банки беспокоили меня достаточно, я бы рассмотрел дополнительный шаг, упомянутый выше, о создании сборки script для специальной версии доступа.


Некоторые примеры прямо из Java

У Swing есть примеры такого типа шаблонов. Обратите внимание, что компоненты GUI имеют void paint(Graphics g). A Graphics предоставляет определенный набор функций. Как правило, g на самом деле является Graphics2D, поэтому вы можете рассматривать его как таковой, если вы этого хотите.

void paint(Graphics g)
{
    Graphics2d g2d = Graphics2d.class.cast(g);
}

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

JList list = new JList();
DefaultListModel model = new DefaultListModel();
list.setModel(model);

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

Добавлена ​​не только дополнительная функциональность, но и оригинальный автор ListModel даже не должен был знать, что эта функциональность может существовать.

Ответ 2

Хорошо, вы можете это сделать. Слово осторожности, хотя...

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

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

Потому что это оказалось бесполезным для меня, я отбросил мое; и я никогда не видел, чтобы кто-то еще это делал и публиковал; так что, насколько я знаю, вам придется сворачивать самостоятельно.

Ответ 3

С помощью Gradle вы можете управлять своими источниками, и я думаю, что макросы препроцессора больше не нужны. Прямо сейчас в каталоге src у вас есть main/java со всеми источниками, но если вам нужны конкретные методы, например. debug и release строит, чтобы делать/или не конкретные вещи, затем создайте debug/java и release/java в src и поместите там YourClass. Обратите внимание, что при этом вам нужно иметь YourClass в debug/java и release/java, но не в main/java.

Ответ 4

Единственный способ использования Java - использовать препроцессор, например, команда PostgresJDBC использует java comment preprocessor для таких манипуляций, здесь пример из их Driver.java

  //#if mvn.project.property.postgresql.jdbc.spec >= "JDBC4.1"
  @Override
  public java.util.logging.Logger getParentLogger() {
    return PARENT_LOGGER;
  }
  //#endif