Эквивалентный из #define в Java?

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

В C это было бы достаточно легко с a #define в заголовке и затем блоках кода, окруженных #ifdefs. Конечно, Java не имеет препроцессора C...

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

Что лучший способ состоит в том, чтобы сделать это в Java?

9
задан Artjom B. 30 March 2015 в 20:44
поделиться

11 ответов

Как говорили другие, в Java не существует такой вещи, как # define / # ifdef. Но что касается вашей проблемы наличия дополнительных внешних библиотек, которые вы бы использовали, если они есть, и не использовали бы, если нет, использование прокси-классов может быть вариантом (если интерфейсы библиотеки не слишком велики).

Мне пришлось сделать это один раз для специальных расширений Mac OS X для AWT / Swing (находится в com.apple.eawt. *). Классы, конечно, есть только в пути к классам, если приложение работает в Mac OS. Чтобы иметь возможность использовать их, но при этом позволять использовать одно и то же приложение на других платформах, я написал простые прокси-классы, которые просто предлагали те же методы, что и исходные классы EAWT. Внутри прокси использовали некоторую рефлексию, чтобы определить, находятся ли реальные классы на пути к классам и будут ли проходить через все вызовы методов. Используя java.lang.reflect. Proxy , вы даже можете создавать и передавать объекты типа, определенного во внешней библиотеке, без их доступности во время компиляции.

Например, прокси для com.apple.eawt.ApplicationListener выглядел так :

public class ApplicationListener {

    private static Class<?> nativeClass;

    static Class<?> getNativeClass() {
        try {
            if (ApplicationListener.nativeClass == null) {
                ApplicationListener.nativeClass = Class.forName("com.apple.eawt.ApplicationListener");
            }

            return ApplicationListener.nativeClass;
        } catch (ClassNotFoundException ex) {
            throw new RuntimeException("This system does not support the Apple EAWT!", ex);
        }
    }

    private Object nativeObject;

    public ApplicationListener() {
        Class<?> nativeClass = ApplicationListener.getNativeClass();

        this.nativeObject = Proxy.newProxyInstance(nativeClass.getClassLoader(), new Class<?>[] {
            nativeClass
        }, new InvocationHandler() {

            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                String methodName = method.getName();

                ApplicationEvent event = new ApplicationEvent(args[0]);

                if (methodName.equals("handleReOpenApplication")) {
                    ApplicationListener.this.handleReOpenApplication(event);
                } else if (methodName.equals("handleQuit")) {
                    ApplicationListener.this.handleQuit(event);
                } else if (methodName.equals("handlePrintFile")) {
                    ApplicationListener.this.handlePrintFile(event);
                } else if (methodName.equals("handlePreferences")) {
                    ApplicationListener.this.handlePreferences(event);
                } else if (methodName.equals("handleOpenFile")) {
                    ApplicationListener.this.handleOpenFile(event);
                } else if (methodName.equals("handleOpenApplication")) {
                    ApplicationListener.this.handleOpenApplication(event);
                } else if (methodName.equals("handleAbout")) {
                    ApplicationListener.this.handleAbout(event);
                }

                return null;
            }

        });
    }

    Object getNativeObject() {
        return this.nativeObject;
    }

    // followed by abstract definitions of all handle...(ApplicationEvent) methods

}

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

Комментарий Питера Лоури: (Извините за редактирование, очень сложно поместить код в комментарий)

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

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    String methodName = method.getName();
    ApplicationEvent event = new ApplicationEvent(args[0]);
    Method method = ApplicationListener.class.getMethod(methodName, ApplicationEvent.class);
    return method.invoke(ApplicationListener.this, event);
}
6
ответ дан 4 December 2019 в 07:14
поделиться

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

Можете ли вы не абстрагироваться от различий, а затем изменять реализацию?

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

.
11
ответ дан 4 December 2019 в 07:14
поделиться

Используйте константу :

На этой неделе мы создаем несколько констант. которые имеют все преимущества использования препроцессора С для определять константы компиляционного времени и условно скомпилированный код.

Java избавилась от всего понятие текстового препроцессора (если ты принимаешь Яву как "потомка" С/С++). Однако, мы можем получить лучшее преимущества хотя бы некоторых из C функции препроцессора на Java: константы и условная компиляция.

2
ответ дан 4 December 2019 в 07:14
поделиться

Используйте для этого свойства.

Используйте такие вещи как Class.forName для идентификации класса.

Не используйте if-statements, когда можно тривиально перевести свойство непосредственно в класс.

.
0
ответ дан 4 December 2019 в 07:14
поделиться

Для достижения одного и того же результата в Java можно использовать различные подходы:

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

5
ответ дан 4 December 2019 в 07:14
поделиться

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

Я с ними не согласен...

Вы CAN используете константы, которые могут быть определены из строки компиляции, и это будет иметь некоторый эффект, но на самом деле не все. (Например, вы не можете иметь вещи, которые не компилируются, но вы все равно хотите, внутри #if 0.... (и нет, комментарии не всегда решают эту проблему, потому что вложенные комментарии могут быть хитрыми...)).

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

Тем не менее, вы всегда можете просто настроить ваш IDE, чтобы бросить ваш java через препроцессор, прежде чем отправить его на javac...

1
ответ дан 4 December 2019 в 07:14
поделиться

Синтаксис Java достаточно близок к C, чтобы вы могли просто использовать препроцессор на C, который обычно поставляется как отдельный исполняемый файл.

Но Java все равно не совсем подходит для компиляции во время компиляции. То, как я уже сталкивался с подобными ситуациями раньше, связано с размышлениями. В вашем случае, с тех пор, как вы обратились к возможно-нет-нет-нет-нет-нет-нет-нет-нет-нет-нет...Настоящая библиотека разбросана по всему коду, я бы сделал класс-обертку, заменил все вызовы библиотеки вызовами класса-обертки, а затем использовал бы отражение внутри класса-обертки для вызова библиотеки, если она присутствует

.
4
ответ дан 4 December 2019 в 07:14
поделиться

"чтобы минимизировать размер моего исполняемого файла"

Что вы имеете в виду под "размером исполняемого файла"?

Если вы имеете в виду количество кода, загружаемого во время выполнения, то вы можете условно загружать классы через загрузчик классов. Таким образом, вы распространяете свой альтернативный код, несмотря ни на что, но на самом деле он загружается только в том случае, если отсутствует библиотека, за которую он выступает. Вы можете использовать адаптер (или аналогичный) для инкапсуляции API, чтобы убедиться, что почти весь ваш код в любом случае одинаков, и один из двух оберточных классов загружается в соответствии с вашим случаем. SPI безопасности Java может дать вам некоторые идеи, как это может быть структурировано и реализовано.

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

.
1
ответ дан 4 December 2019 в 07:14
поделиться

В зависимости от того, что вы делаете (не совсем достаточно информации), вы можете сделать что-нибудь вроде этого:

interface Foo
{
    void foo();
}

class FakeFoo
    implements Foo
{
   public void foo()
   {
       // do nothing
   }
}

class RealFoo
{
    public void foo()
    {
        // do something
    }
}

и затем предоставить класс для реферирования инстанцирования:

class FooFactory
{
    public static Foo makeFoo()
    {
        final String   name;
        final FooClass fooClass;
        final Foo      foo;

        name     = System.getProperty("foo.class");
        fooClass = Class.forName(name);
        foo      = (Foo)fooClass.newInstance();

        return (foo);
    }
}

Затем запустить java с -Dfoo. name=RealFoo|FakeFoo

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

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

.
0
ответ дан 4 December 2019 в 07:14
поделиться

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

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

Если вы хотите, чтобы одна версия обнаруживала существование библиотеки и использовала ее, если она доступна, то вы ДОЛЖНЫ иметь весь код для доступа к ней в вашем распространяемом коде - вы не можете ее обрезать. Так как вы приравниваете свою проблему к использованию #define, я предположил, что это не ваша цель - вы хотите поставлять 2 версии (Единственный способ, которым #define может работать)

Так что, с 2 версиями вы можете определить библиотекуInterface. Это может быть либо объект, который обёртывает вашу библиотеку и пересылает все вызовы к ней для вас, либо интерфейс - в любом случае этот объект ДОЛЖЕН существовать во время компиляции для обоих режимов.

public LibraryInterface getLibrary()
{
    if(LIBRARY_EXISTS) // final boolean
    {
        // Instantiate your wrapper class or reflectively create an instance             
        return library; 
    }
    return null;
}

Теперь, когда вы хотите использовать вашу библиотеку (в случаях, когда у вас была бы #ifdef на C), у вас есть следующее:

if(LIBRARY_EXISTS)
    library.doFunc()

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

Если ваша библиотека является готовой библиотекой, предоставляемой третьим лицом, вам, возможно, придется сделать библиотеку оберточным классом, который пересылает свои вызовы к вашей библиотеке. Так как ваша библиотека никогда не инстанцируется, если LIBRARY_EXISTS ложна, она даже не должна быть загружена во время выполнения (Heck, она даже не должна быть скомпилирована, если JVM достаточно умна, так как она всегда защищена конечной константой), но помните, что обёртка ДОЛЖНА быть доступна во время компиляции в обоих случаях.

.
0
ответ дан 4 December 2019 в 07:14
поделиться

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

-121--1850272-

Семантика For_each не вписывается в то, что вы пытаетесь сделать. накапливать делает именно то, что вы пытаетесь, использовать это вместо этого.

-121--3543235-

Если это помогает взглянуть на j2me polk или Использование директив препроцессора в плагине BlackBerry JDE для затмения?

Это приложение для мобильных устройств, но это может быть повторно использовано нет?

0
ответ дан 4 December 2019 в 07:14
поделиться
Другие вопросы по тегам:

Похожие вопросы: