相当于Java中的#define?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1943021/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 01:39:07  来源:igfitidea点击:

Equivalent of #define in Java?

javapreprocessorc-preprocessor

提问by Justin

I'm writing a library that needs to have some code if a particular library is included. Since this code is scattered all around the project, it would be nice if users didn't have to comment/uncomment everything themselves.

如果包含特定库,我正在编写一个需要一些代码的库。由于此代码分散在整个项目中,如果用户不必自己注释/取消注释一切,那就太好了。

In C, this would be easy enough with a #definein a header, and then code blocks surrounded with #ifdefs. Of course, Java doesn't have the C preprocessor...

在 C 中,这很容易#define在标题中使用 a,然后用#ifdefs. 当然,Java 没有 C 预处理器......

To clarify - several external libraries will be distributed with mine. I do not want to have to include them all to minimize my executable size. If a developer does include a library, I need to be able to use it, and if not, then it can just be ignored.

澄清一下 - 几个外部库将与我的一起分发。我不想为了最小化我的可执行文件大小而将它们全部包含在内。如果开发人员确实包含了一个库,我需要能够使用它,如果没有,那么它可以被忽略。

What is the best way to do this in Java?

在 Java 中执行此操作的最佳方法是什么?

采纳答案by Simon Lehmann

As other have said, there is no such thing as #define/#ifdef in Java. But regarding your problem of having optional external libraries, which you would use, if present, and not use if not, using proxy classes might be an option (if the library interfaces aren't too big).

正如其他人所说,Java 中没有 #define/#ifdef 这样的东西。但是,关于您拥有可选外部库的问题,如果存在,您将使用,如果没有,则不使用,使用代理类可能是一种选择(如果库接口不太大)。

I had to do this once for the Mac OS X specific extensions for AWT/Swing (found in com.apple.eawt.*). The classes are, of course, only on the class-path if the application is running on Mac OS. To be able to use them but still allow the same app to be used on other platforms, I wrote simple proxy classes, which just offered the same methods as the original EAWT classes. Internally, the proxies used some reflection to determine if the real classes were on the class-path and would pass through all method calls. By using the java.lang.reflect.Proxyclass, you can even create and pass around objects of a type defined in the external library, without having it available at compile time.

我不得不为 AWT/Swing 的 Mac OS X 特定扩展(在 com.apple.eawt.* 中找到)执行此操作。当然,如果应用程序在 Mac OS 上运行,这些类只在类路径上。为了能够使用它们但仍允许在其他平台上使用相同的应用程序,我编写了简单的代理类,它只提供与原始 EAWT 类相同的方法。在内部,代理使用一些反射来确定真正的类是否在类路径上并通过所有方法调用。通过使用java.lang.reflect.Proxy类,您甚至可以创建和传递外部库中定义的类型的对象,而无需在编译时使其可用。

For example, the proxy for com.apple.eawt.ApplicationListener looked like this:

例如, 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

}

All this only makes sense, if you need just a few classes from an external library, because you have to do everything via reflection at runtime. For larger libraries, you probably would need some way to automate the generation of the proxies. But then, if you really are that dependent on a large external library, you should just require it at compile time.

如果您只需要来自外部库的几个类,所有这些才有意义,因为您必须在运行时通过反射来完成所有工作。对于较大的库,您可能需要某种方式来自动生成代理。但是,如果你真的那么依赖一个大型外部库,你应该只在编译时要求它。

Comment by Peter Lawrey: (Sorry to edit, its very hard to put code into a comment)

Peter Lawrey 的评论:(抱歉编辑,很难将代码放入评论中)

The follow example is generic by method so you don't need to know all the methods involved. You can also make this generic by class so you only need one InvocationHandler class coded to cover all cases.

以下示例按方法是通用的,因此您无需了解所有涉及的方法。您还可以按类使其通用,因此您只需要编码一个 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);
}

回答by Andrew Hare

Use a constant:

使用常量

This week we create some constants that have all of the benefits of using the C preprocessor's facilities to define compile-time constants and conditionally compiled code.

Java has gotten rid of the entire notion of a textual preprocessor (if you take Java as a "descendent" of C/C++). We can, however, get the best benefits of at least some of the C preprocessor's features in Java: constants and conditional compilation.

本周我们创建了一些常量,它们具有使用 C 预处理器的工具来定义编译时常量和条件编译代码的所有好处。

Java 已经摆脱了文本预处理器的整个概念(如果您将 Java 视为 C/C++ 的“后代”)。然而,我们可以从 Java 中至少一些 C 预处理器的特性中获得最大的好处:常量和条件编译。

回答by S.Lott

Use properties to do this kind of thing.

使用属性来做这种事情。

Use things like Class.forName to identify the class.

使用 Class.forName 之类的东西来标识类。

Do not use if-statements when you can trivially translate a property directly to a class.

当您可以简单地将属性直接转换为类时,不要使用 if 语句。

回答by Steve Emmerson

There's no way to do what you want from within Java. You could preprocess the Java source files, but that's outside the scope of Java.

在 Java 中没有办法做你想做的事。您可以预处理 Java 源文件,但这超出了 Java 的范围。

Can you not abstract the differences and then vary the implementation?

你不能抽象差异然后改变实现吗?

Based on your clarification, it sounds like you might be able to create a factory method that will return either an object from one of the external libraries or a "stub" class whose functions will do what you would have done in the "not-available" conditional code.

根据您的澄清,听起来您可能能够创建一个工厂方法,该方法将返回来自外部库之一的对象或“存根”类,其功能将执行您在“不可用”中所做的工作" 条件码。

回答by Brian Postow

I don't believe that there really is such a thing. Most true Java users will tell you that this is a Good Thing, and that relying on conditional compilation should be avoided at almost all costs.

我不相信真的有这样的事情。大多数真正的 Java 用户会告诉您这是一件好事,并且几乎不惜一切代价都应该避免依赖条件编译。

I'm don't really agree with them...

我真的不同意他们...

You CAN use constants that can be defined from the compile line, and that will have some of the effect, but not really all. (For example, you can't have things that don't compile, but you still want, inside #if 0... (and no, comments don't always solve that problem, because nesting comments can be tricky...)).

您可以使用可以从编译行定义的常量,这会产生一些影响,但不是全部。(例如,你不能有不能编译的东西,但你仍然想要,在 #if 0 中......(不,注释并不总是能解决这个问题,因为嵌套注释可能很棘手...... ))。

I think that most people will tell you to use some form of inheritance to do this, but that can be very ugly as well, with lots of repeated code...

我认为大多数人会告诉你使用某种形式的继承来做到这一点,但这也可能非常丑陋,有很多重复的代码......

That said, you CAN always just set up your IDE to throw your java through the pre-processor before sending it to javac...

也就是说,你总是可以设置你的 IDE 在将它发送到 javac 之前通过预处理器抛出你的 java ......

回答by Vlad Gudim

In Java one could use a variety of approaches to achieve the same result:

在 Java 中,可以使用多种方法来实现相同的结果:

The Java way is to put behaviour that varies into a set of separate classes abstracted through an interface, then plug the required class at run time. See also:

Java 的方式是将变化的行为放入通过接口抽象的一组单独的类中,然后在运行时插入所需的类。也可以看看:

回答by Steve Jessop

"to minimize my executable size"

“最小化我的可执行文件大小”

What do you mean by "executable size"?

“可执行大小”是什么意思?

If you mean the amount of code loaded at runtime, then you can conditionally load classes through the classloader. So you distribute your alternative code no matter what, but it's only actually loaded if the library that it stands in for is missing. You can use an Adapter (or similar) to encapsulate the API, to make sure that almost all of your code is exactly the same either way, and one of two wrapper classes is loaded according to your case. The Java security SPI might give you some ideas how this can be structured and implemented.

如果您指的是运行时加载的代码量,那么您可以通过类加载器有条件地加载类。因此,无论如何您都可以分发替代代码,但只有在它所代表的库丢失时才会真正加载它。您可以使用适配器(或类似的)来封装 API,以确保您的几乎所有代码都完全相同,并且根据您的情况加载两个包装类之一。Java 安全 SPI 可能会为您提供一些如何构建和实现它的想法。

If you mean the size of your .jar file, then you can do the above, but tell your developers how to strip the unnecessary classes out of the jar, in the case where they know they aren't going to be needed.

如果您的意思是 .jar 文件的大小,那么您可以执行上述操作,但告诉您的开发人员如何从 jar 中删除不必要的类,以防他们知道不需要它们。

回答by JaakkoK

Well, Java syntax is close enough to C that you could simply use the C preprocessor, which is usually shipped as a separate executable.

好吧,Java 语法与 C 非常接近,您可以简单地使用 C 预处理器,它通常作为单独的可执行文件提供。

But Java isn't really about doing things at compile time anyway. The way I've handled similar situations before is with reflection. In your case, since your calls to the possibly-non-present library are scattered throughout the code, I would make a wrapper class, replace all the calls to the library with calls to the wrapper class, and then use reflection inside the wrapper class to invoke on the library if it is present.

但无论如何,Java 并不是真的要在编译时做事。我以前处理类似情况的方式是反思。在您的情况下,由于您对可能不存在的库的调用分散在整个代码中,我将创建一个包装类,用对包装类的调用替换对库的所有调用,然后在包装类中使用反射调用库(如果存在)。

回答by TofuBeer

Depending on what you are doing (not quite enough information) you could do something like this:

根据您在做什么(信息不够),您可以执行以下操作:

interface Foo
{
    void foo();
}

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

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

and then provide a class to abstract the instantiation:

然后提供一个类来抽象实例化:

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

Then run java with -Dfoo.name=RealFoo|FakeFoo

然后用 -Dfoo.name=RealFoo|FakeFoo 运行 java

Ignored the exception handling in the makeFoo method and you can do it other ways... but the idea is the same.

忽略了 makeFoo 方法中的异常处理,你可以用其他方式来做……但想法是一样的。

That way you compile both versions of the Foo subclasses and let the developer choose at runtime which they wish to use.

这样您就可以编译 Foo 子类的两个版本,并让开发人员在运行时选择他们希望使用的版本。

回答by Bill K

I see you specifying two mutually exclusive problems here (or, more likely, you have chosen one and I'm just not understanding which choice you've made).

我看到你在这里指定了两个相互排斥的问题(或者,更有可能的是,你选择了一个,我只是不明白你做了哪个选择)。

You have to make a choice: Are you shipping two versions of your source code (one if the library exists, and one if it does not), or are you shipping a single version and expecting it to work with the library if the library exists.

您必须做出选择:您是发布源代码的两个版本(一个如果库存在,一个如果不存在),或者您发布一个版本并期望它与库一起使用(如果库存在) .

If you want a single version to detect the library's existence and use it if available, then you MUST have all the code to access it in your distributed code--you cannot trim it out. Since you are equating your problem with using a #define, I assumed this was not your goal--you want to ship 2 versions (The only way #define can work)

如果您想要一个单一版本来检测库的存在并在可用时使用它,那么您必须在分布式代码中拥有访问它的所有代码——您不能删除它。由于您将问题等同于使用 #define,我认为这不是您的目标——您想发布 2 个版本(#define 可以工作的唯一方法)

So, with 2 versions you can define a libraryInterface. This can either be an object that wraps your library and forwards all the calls to the library for you or an interface--in either case this object MUST exist at compile time for both modes.

因此,您可以使用 2 个版本定义 libraryInterface。这可以是包装您的库并为您转发对库的所有调用的对象,也可以是一个接口——在任何一种情况下,对于两种模式,该对象都必须在编译时存在。

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

Now, when you want to USE your library (cases where you would have had a #ifdef in C) you have this:

现在,当你想使用你的库时(你在 C 中有 #ifdef 的情况)你有这个:

if(LIBRARY_EXISTS)
    library.doFunc()

Library is an interface that exists in both cases. Since it's always protected by LIBRARY_EXISTS, it will compile out (should never even load into your class loader--but that's implementation dependent).

库是两种情况下都存在的接口。由于它始终受 LIBRARY_EXISTS 保护,因此它会编译出来(甚至不应该加载到您的类加载器中——但这取决于实现)。

If your library is a pre-packaged library provided by a 3rd party, you may have to make Library a wrapper class that forwards it's calls to your library. Since your library wrapper is never instantiated if LIBRARY_EXISTS is false, it shouldn't even be loaded at runtime (Heck, it shouldn't even be compiled in if the JVM is smart enough since it's always protected by a final constant.) but remember that the wrapper MUST be available at compile time in both cases.

如果您的库是由第 3 方提供的预打包库,则您可能必须将 Library 设为包装类,将其调用转发到您的库。因为如果 LIBRARY_EXISTS 为 false,你的库包装器永远不会被实例化,它甚至不应该在运行时加载(哎呀,如果 JVM 足够智能,它甚至不应该被编译,因为它总是受最终常量保护。)但请记住在这两种情况下,包装器都必须在编译时可用。