Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Conditionally Remove Java Methods at Compile-Time

I am trying to achieve something similar to the C# preprocessor. I am aware that Java does NOT have the same preprocessor capabilities, and am aware that there are ways to achieve similar results using design patterns such as Factory. However, I am still interested in finding a solution to this question.

Currently, what I do is create a class that contains several static final boolean attributes, such as the following example:

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

I then use this in the following manner:

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

So far so good, this solves my problem (the example above is trivial, but I do use this in other instances where it is helpful). My question is, would there be a way to place the conditional around an entire method, so that the method itself would be unavailable given the correct "Preprocessor" variables? For example, I would like to be able to make a specific constructor available only for packages that are given "Full Access", as follows:

public ClassName()
{
    // do things
}

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

Again, I am aware of the limitations (or design decisions) of Java as a language, and am aware that in most circumstances this is unnecessary. As a matter of fact, I have considered simply creating these "extra" methods and placing the entire code of them within a conditional, while throwing an Exception if the conditional is not active, but that is a very crude solution that does not seem helpful to my programmers when I make these libraries available to them.

Thank you very much in advance for any help.

Edit:

To complement the question, the reason why I am attempting to do this is that by using exceptions as a solution, the IDE would display methods as "available" when they are actually not. However, again, it might just be a case of my being ignorant of Java.

The reasons for my wanting to do this are primarily so that I may have more than one public interface available, say, one restrictive where control is tighter within the methods, and one more permissive where direct alteration of attributes is allowed. However, I do also want to be able to actively remove portions of code from the .class, for instance, in a Product Line development approach where certain variants are not available.

Edit2.:

Furthermore, it is important to note that I will be generating the documentation conditionally as well. Therefore, each compiled version of the packages would have its own documentation, containing only that which is actually available.

like image 218
Fledgling Pidgeon Avatar asked Mar 27 '17 17:03

Fledgling Pidgeon


People also ask

Does Java have conditional compilation?

So Java does have its own conditional compilation mechanism. What if we want to use debugging code similar to this, but have the condition applied at runtime? We can use System. properties (Section 2.3) to fetch a variable.

Does Java have Ifdef?

Java does not include any kind of preprocessor like the C cpp preprocessor. It may seem hard to imagine programming without #define, #include, and #ifdef, but in fact, Java really does not require these constructs.


3 Answers

the only way in Java to reach that is to use preprocessor, for instance PostgresJDBC team uses java comment preprocessor for such manipulations, here is example from their Driver.java

  //#if mvn.project.property.postgresql.jdbc.spec >= "JDBC4.1"
  @Override
  public java.util.logging.Logger getParentLogger() {
    return PARENT_LOGGER;
  }
  //#endif
like image 60
Igor Maznitsa Avatar answered Sep 28 '22 00:09

Igor Maznitsa


This answer is based partially on the comments you have left on the question and on Mark's answer.

I would suggest that you do this using Java interfaces which expose just the API that you desire. When you need a less restrictive API contract, extend an interface or create a separate implementation of an existing interface to get what you need.

public interface A
{
    void f();
}

A above is your general API. Now you want to have some special extra methods to test A or to debug it or manipulate it or whatever...

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

Also, Java now supports default method implementations for interfaces which might be useful to you depending on how you implement your API. They take the following form...

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

You can read more about default methods on Oracle's page about it here.

In your API, your general distribution of it could include A and omit B entirely, and omit any implementations that offer the special access; then you can include B and special implementations for the special access version of the API you mentioned. This would allow plain old Java objects, nothing different to the code other than an extra interface and maybe an extra implementation of it. The custom part would just be in your packaging of the library. If you want to hand someone a "non-special" low-access version, hand them a jar that does not include B and does not include any possible BImplementation, possibly by having a separate build script.

I use Netbeans for my Java work, and I like to let it use the default build scripts that it auto generates. So if I were doing this and I were doing it in Netbeans, I would probably create two projects, one for base API and one for special-access API, and I would make the special-access one dependent on the base project. That would leave me with two jars instead of one, but I would be fine with that; if two jars bothered me enough I would go through the extra step mentioned above of making a build script for the special access version.


Some examples straight from Java

Swing has examples of this kind of pattern. Notice that GUI components have a void paint(Graphics g). A Graphics gives you a certain set of functionality. Generally, that g is actually a Graphics2D, so you can treat it as such if you so desire.

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

Another example is with Swing component models. If you use a JList or a JComboBox to display a list of objects in a GUI, you probably do not use the default model it comes with if you want to change that list over time. Instead, you create a new model with added functionality and inject it.

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

Now your JList model has extra functionality that is not normally apparent, including the ability to add and remove items easily.

Not only is extra functionality added this way, but the original author of ListModel did not even need to know that this functionality could exist.

like image 38
Loduwijk Avatar answered Sep 28 '22 02:09

Loduwijk


Well, you can make it happen. A word of caution, though...

I can only think of one time when I thought this kind of approach was the best way, and it turned out I was wrong. The case of changing a class's public interface especially looks like a red flag to me. Throwing an exception when the access level isn't high enough to invoke the method might be more code-friendly.

But anyway, when I thought I wanted a preprocessor, what I did was to write one. I created a custom annotation to place on conditionally-available methods, grabbed a Java parser and wrote a little program that used the parser to find and remove methods that have the annotation. Then add that (conditionally) to the build process.

Because it turned out to be useless to me, I discarded mine; and I've never seen anyone else do it and publish it; so as far as I know you'd have to roll your own.

like image 29
Mark Adelsberger Avatar answered Sep 28 '22 02:09

Mark Adelsberger