views:

658

answers:

11

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 #define in a header, and then code blocks surrounded with #ifdefs. Of course, Java doesn't have the C preprocessor...

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?

+2  A: 

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.

Andrew Hare
I don't think this is what he is looking for. He doesn't want symbolic constants, but an equivalent to conditional compilation. Not knowing java, I haven't a clue, though...
dmckee
what about conditional compilation? How do you do that in java?
Russell
The article I linked discusses this.
Andrew Hare
As a quick note on the linked article, the whole "make an interface for your constants and implement it" is outdated and ludicrous now that there is `import static`.
Anon.
The conditional compile still only works if the everything compiles. if, when the then branch is true, the else branch doesn't compile, because those methods don't exist, or something, then it won't compile. #if solves this nicely.
Brian Postow
What Brian said is exactly the issue.
Justin
Considered method stubs?
Anon.
Yes, you don't really want non-compiling code in your system. Use version control to target a build, or you can use method stubs. Worst case if you really MUST have code that links to a library that is not available at compile time then you can use reflection.
Bill K
A: 

Use properties to do this kind of thing.

Use things like Class.forName to identify the class.

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

S.Lott
+9  A: 

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.

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.

Steve Emmerson
+1: This issue sounds like it can be(might be) possible to modify the solution architecture design to help with this.
Russell
The Java compiler will optimize away conditional checks based on constant values. Thus it's definitely possible.
Anon.
@Anon: But it still compiles both branches. it just optimizes away the unused code, right? Also, a LOT of the time, re-structuring your architecture to eliminate these problems can get REALLY messy. If you have a bunch of random places that you want #ifdefed... you end up with one base class, then a bunch of sub-classes, with helper functions, and it just becomes a mess.
Brian Postow
You have one class, no helper functions, and a bunch of symbolic constants. I'm not seeing the need for subclassing anything.
Anon.
We need more details in order to tell if a design solution exists.
Steve Emmerson
@Anon, Sorry, I was conflating you with the original answerer (Steve) and Russell, The optimization doesn't solve the "not compiling" problem. the subclassing and helpers are a response to the architectural /abstraction solutions.
Brian Postow
+1  A: 

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.

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

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

Brian Postow
+5  A: 

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

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:

Totophil
+1 for Dependency Injection. Imagine wiring up your objects via an XML file (as an example) and then using a different XML file for your various situations.
Michael Easter
+1  A: 

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

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.

Steve Jessop
The library will be used in Android applications, so I don't want to include anything that is unnecessary.
Justin
This doesn't meet the parameters of your original question. If you are linking at runtime, you still HAVE to have the code that calls the library compiled in so #ifdef wouldn't help anyway. If you are not linking at runtime but compiling for different targets, then if(CONSTANT) will suffice. Which one is the actual problem?
Bill K
No, the constant won't suffice. For example, `if(false) {LibraryIDontHave.action();}` will not compile.
Justin
+4  A: 

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

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.

jk
+4  A: 

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

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.Proxy class, you can even create and pass around objects of a type defined in the external library, without having it available at compile time.

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

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)

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.

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);
}
Simon Lehmann
A: 

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

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

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

TofuBeer
A: 

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)

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.

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:

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

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.

Bill K
A: 

If it helps have a look at j2me polish or http://stackoverflow.com/questions/1383277/using-preprocessor-directives-in-blackberry-jde-plugin-for-eclipse

this is for mobiles app but this can be reused no ?

rzr