0

I am wondering if it is possible for a developer to write a class so that when some other developer instantiates an object of the aforementioned class, a call on a specific method of the class gets added to his code.

Let me be more specific. Let's say that I create a class named A with the following structure:

public class A {

    public A() {
        // Some instantiations
    }

    // Code class

    // Method that always should be added
    public void method() {
        // Code
    }
}

I wrap this class into a package and some other developer instantiates class A in a new object. When he does that, a call on method() is added right after the instantiation, probably with a comment right above it:

// Code
A myA = new A();
// Do some operations on A

// You should always wrap up with a call on method
myA.method();

// Code

This is just to inform the developer not to forget to call it at a specific place in his code.

The reason I am asking this is because I have created a class that contains a queuing mechanism that will be used in a large project as also as in future projects. I need to force every future developer to reference a method called close() at the end of his code, to terminate the connection to the queuing system. If he forgets to do that (and there is very high possibility that he/she does that), the connections will be open, adding a huge overhead to the total project.

If there is no way to implement this (and to my knowledge, I can't think of anything), what's the logic behind to not include something like this in standard JDK by Oracle? I can think of examples of JDK's own libraries that could be benefited by this (e.g. closing a PrintWriter when you are finished writing to a file), so I don't think that this is a misunderstanding from my side of Java's principles.

UPDATE

Because I think I accidentally started a flame war...

I do think that a good API is written in a way that does procedures like this automatically. Instantiate an object/call a method and the rest are done automatically. But for my case, I need something like C++'s destructors: a way to force or otherways warn the developer to close the connection to the queuing system he created.

UPDATE 2

The project is unfortunately written in JDK 7 that does not support lambda functions.

UPDATE 3

As it turns out my assumption was true and there is no standard Java feature (except maybe of the lambda functions) that can be leveraged. Can I add at least a warning on the other developer's class that he forgot to close the connection?

M. Prokhorov
  • 3,894
  • 25
  • 39
Lefteris008
  • 899
  • 3
  • 10
  • 29
  • 1
    I would be really grateful if you take a moment and comment on the reason of downvoting me. – Lefteris008 Jul 11 '17 at 13:24
  • And this is where C++ people will be like: *"Too bad Java doesn't have destructors, eh?"* :-) – domsson Jul 11 '17 at 13:25
  • @domdom correct, this a destructor like method. – Lefteris008 Jul 11 '17 at 13:25
  • 2
    I didn't downvote you but what you need is try-with resources. I am assuming you are implementing autocloseable. – bichito Jul 11 '17 at 13:26
  • Seriously though, it might be fine to simply require the user (programmer) to read the docs and follow the required procedure, just as we should remember to close file handles and other resources. However, there are probably better solutions to this (let's wait for answers to come in); I just wanted to say that it isn't such a bad thing to require the user to close resources manually. – domsson Jul 11 '17 at 13:28
  • 1
    @domdom How about the [finalize method](https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#finalize--) – OH GOD SPIDERS Jul 11 '17 at 13:28
  • @OHGODSPIDERS Now, would you look at that! Thanks, I just learned something new and useful again. You might want to consider writing up an answer using that; I would personally be interested to read it. – domsson Jul 11 '17 at 13:29
  • finalizers is worse than a rabbit hole. There are a zillion articles about avoiding them. But it is a good joke – bichito Jul 11 '17 at 13:32
  • So you realize that finalizers is just a bad joke: there is no telling when they are called. So your primary goal of letting people know is dead in the water. – bichito Jul 11 '17 at 13:41
  • OP, I think [this answer](https://stackoverflow.com/a/158216/3316645) to a question on `finalize()` might be of interest to you. – domsson Jul 11 '17 at 13:42
  • 2
    OP you might want to consider leveraging [AutoClosable](https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html) interface. At least the Eclipse Java compiler can mark an error for forgetting to use it in try with resource block. – Markus Jul 11 '17 at 13:45
  • 1
    What you are trying to do is to put in code "good management". As pointed out you do not need lambdas, there is the autocloseable interface. – bichito Jul 11 '17 at 13:53
  • @efekctive, with lambdas one can force correct usage, because it would be the only usage there is. AutoCloseable is mostly a suggestion and a handy tool for try-with-resources. – M. Prokhorov Jul 11 '17 at 14:02
  • But I'm looking as OP wants to only warn about non-closed resource, the AutoCloseable is the right tool for that. – M. Prokhorov Jul 11 '17 at 14:04
  • @M.Prokhorov as you point out yourself lambdas can be easily replaced with anonymous classes. I upvoted you even though I was the first to suggest try-with resources. I was just pointing out all the non-finalize options. Finalize methods do not solve the problem and open a can of worms – bichito Jul 11 '17 at 14:26

4 Answers4

4

You should leverage the try-with-resouce feature which was introduced in Java7.

Your aforementioned class A should implement the Interface AutoClosable then:

public class A implements AutoClosable {
    public void close() {
        // Do closing
    }
}

Using instances of A should happen in a try-with resource block:

try(A a = new A()) {
    // Do something
}

This ensures the close method is called after reaching the end of the try block.

Finally at least the eclipse-compiler can mark forgetting using A in a try-with-resource block as an error.

Markus
  • 2,071
  • 4
  • 22
  • 44
  • In eclispe this is usually just a warning. – M. Prokhorov Jul 11 '17 at 13:53
  • After reading through all comments and answers, I think this is probably the best solution. Maybe, to account for OPs third update (*"Can I add at least a warning [...]?"*), it couldn't hurt to also implement `finalize()` simply to check if `close()` was actually called, and print some errors to `stderr` otherwise? – domsson Jul 11 '17 at 13:54
  • @domdom, we are not guaranteed that `finalize` is called at any point, which is why it's generally considered as unreliable mechanism. – M. Prokhorov Jul 11 '17 at 13:54
  • Yes, but you can increase the warning to an error. @M.Prokhorov – Markus Jul 11 '17 at 13:55
  • Sure, but if it is only [being used to spit out some warning](https://stackoverflow.com/a/158216/3316645), then it surely couldn't hurt? – domsson Jul 11 '17 at 13:55
  • 1
    Great, this is what I need. Thank you! – Lefteris008 Jul 11 '17 at 13:58
  • @Markus, true, but we can also reduce the warning down to no-op. I like try-with-resources, but it unfortunately doesn't really enforce anything in particular. – M. Prokhorov Jul 11 '17 at 13:59
  • Sure, but in a company I think such rules should be enforced. – Markus Jul 12 '17 at 05:47
3

What you probably want to implement is a sort of "Use with" pattern, where you only expose a single public method on your class that will accemp a "Use strategy" for your object, while giving you as an author full control over instantiation and destruction of your resource object.

Consider this resource class:

public class ExpensiveResource {
  private ExpensiveResource() {} // private so only we can get an instance

  public void destroy() { // a method we always want to call after we're done
    <...>
  }

  <...more operations with resource here...>

  // This is the only actual entry point where anyone can access instances of your resource.
  // As you can see, destroy() is always called no matter what.
  public static void use(UsageStrategy strategy) {
    ExpensiveResource resource = new ExpensiveResource();
    try {
      strategy.useResource(resource);
    }
    finally {
      resource.destroy();
    }
  }

  public static interface UsageStrategy extends Consumer<ExpensiveResource> {
    @Override
    default void accept(ExpensiveResource arg0) {
      useResource(arg0);
    }

    void useResource(ExpensiveResource resource);
  }
}

And anyone who wants to use your resource will need to call it like this:

ExpensiveResource.use(resource -> {
  var a = resource.operation1();
  resource.operation2();
  resource.operation3(a);
  <... etc. ...>
});

If you are using Java7 still, all you need for it to work is to make an anonymous class:

ExpensiveResource.use(new UsageStrategy() {
  public void useResource(ExpensiveResource resource) {
    resource.operation1();
    <... etc. ...>
  }
});

It is far less elegant due to boilerplate syntax, but the basic idea is the same, and you can start enjoying the benefits of Java8 as soon as you upgrade.

(Note that I extended UsageStrategy from java.util.function.Consumer above. Java 7 will not have this class, but you can just remove extends and default a method).

M. Prokhorov
  • 3,894
  • 25
  • 39
  • As I wrote to the other answer, the solution you provide is good but the project is being written in JDK 7 that does not support lambda functions. – Lefteris008 Jul 11 '17 at 13:41
  • @Lefteris008, you can use anonymous classes for now, you will just need to migrate later. I'll update my answer. – M. Prokhorov Jul 11 '17 at 13:42
0

You are going down the wrong rabbit hole here. Meaning: good APIs are easy to be used correctly, and hard to be used the wrong way.

If you want that new A() is followed by some doSomething() then the solution goes like this:

  • make that constructor private
  • put a static helper method into A that does

this:

public static A makeA() {
   A result = new A();
   doSomething();
   return result;
}

Or something alike. In other words: don't strive for complicated, non-standard, dirty-hackishly looking workarounds. Instead: make it impossible to do the wrong thing.

GhostCat
  • 137,827
  • 25
  • 176
  • 248
  • 1
    You are right about the principles of a good API. But the logic behind my reasoning is this: create a queue helper object, queue an unknown number of messages across your method (and to subsequent methods that get called from your method) and finally **close** the connection. Like @domdom wrote above, I need something like C++ destructors. – Lefteris008 Jul 11 '17 at 13:28
0

There is no such feature in any IDE that I know, but most of them have plugins. you can write your own plugin to implement this.

Other option is redesign your API like this

class Main {
    public static void main(String[] arg) {

        String res = QueueService.doWithQueue(q -> q.someMethod());

    }
}

class QueueService {
    public static <T> T doWithQueue(DoWithQueue<T> m) {
        MyQueue myQueue = new MyQueue();
        T t = m.method(myQueue);
        myQueue.close();
        return t;
    }
}

interface DoWithQueue<T> {
    T method(MyQueue m);
}

class MyQueue {
    public void close() {
    }

    public String someMethod() {
        return "something";
    }
}

And do not let to create MyQueue other way tan thru QueueService

talex
  • 17,973
  • 3
  • 29
  • 66
  • Very good point here but the project is being built in JDK 7 that does not support lambda functions. – Lefteris008 Jul 11 '17 at 13:40
  • @Lefteris008 You can use anonymous class instead of lambda. Of course you code won't by so clean then. – talex Jul 11 '17 at 13:44