views:

116

answers:

4

I would like to write the following code:

boolean found = false;
search(new SearchCallback() {
  @Override void onFound(Object o) { found = true; }
});

Obviously this is not allowed, since found needs to be final. I can't make found a member field for thread-safety reasons. What is the best alternative? One workaround is to define

final class MutableReference<T> {
  private T value;
  MutableReference(T value) { this.value = value; }
  T get() { return value; }
  void set(T value) { this.value = value; }
}

but this ends up taking a lot of space when formatted properly, and I'd rather not reinvent the wheel if at all possible. I could use a List<Boolean> with a single element (either mutating that element, or else emptying the list) or even a Boolean[1]. But everything seems to smell funny, since none of the options are being used as they were intended.

What is a reasonable way to do this?

+4  A: 

I tend to do the boolean[1] method you mentioned:

final boolean[] found = {false};
search(new SearchCallback() {
  @Override void onFound(Object o) { found[0] = true; }
});

It's a bit hackish, but it tends to be the closest thing to what you actually want

Michael Mrozek
@Michael Mrozek: this only works as long as you only have one thread in your program accessing the primitive array (which the callback hint is not the case btw). There's no way to have a "volatile" primitive array (that's not in Java) so you **must** use another synchronization mean: AtomicBoolean, AtomicReference, AtomicIntegerArray, use heavy synchronization, etc. But what you've posted above is most likely utterly broken code.
Webinator
@WizardOfOdds, you are making a wild assumption that this search executes in a different thread. There is nothing in the question which suggests this. A callback is often used even if there is only one thread (think of FileFilter as an obvious API example). In fact, the whole question wouldn't make sense if the call was to an asyncronous method.
Yishai
@Yishai: saying that this *"whole question wouldn't make sense if the call was to an asynchronous method"* [sic] shows an amazing level of intellectual dishonesty: I've got **much** more callbacks in the 200KLOC codebase I'm working here made from other threads than from the same thread. It's basically the whole point of a callback: you don't really know who nor when nor from where it's going to be called. I'm bringing here a very real concern of uttermost importance and instead of acknowledging it you find excuse to justify your bogus answer. This is very sad, so is your upvote.
Webinator
@WizardOfOdds, I'm sorry but you just don't get the point here. If the local variable is intended to capture anything relevant it would have to be a synchronous call. If it is asynchronous, capturing the result into any local variable would be useless as that thread would have moved on. The question only makes sense if the code is syncronous, and therefore very unlikely to involve a second thread to call the callback. Note that *everyone* answering this question assumes synchronicity.
Yishai
@WizardOfOdds Lets us imagine that you set the found[0] = true in multiple threads without volatile or synchronization, what do you imagine will happen? it might get set to true once instead of twice? it might be set to true in the first thread before the second or visa-versa? You said it is a *must*, but I am not sure you know why.
Peter Lawrey
+2  A: 

All solutions are indeed hackish, but the array is the "standard" textbook way of handling it, as even pre-generics it was typesafe.

Another option in this situation is to make a private class like so:

   private class Searcher implements SearchCallback {
        private boolean found;
        @Override public void onFound(Object o) { found = true; }
        public boolean search() {
              OuterClass.this.search(this);
              return found;
        }
   }

And then use it like so:

  boolean found = new Searcher().search();

Edit: If I understand Tom's comment correctly, he is suggesting this as an alternative

 public void foo() { //This is the method that enclosed the code in your question
     new SearchCallBack() {
         private boolean found;
         @Override public void onFound(Object o) { found = true; }
         {
            //The code that was before this in your method
            search(this);
            //The code that was after this in your method
         }
     };
 }

I think that is more hackish and I would really find such code unusual, but it is definitely worth knowing that it is an option.

Yishai
Downvoter: Care to comment?
Yishai
Could also be written as an anonymous inner class. An instance initialiser can be used to call the `search` method.
Tom Hawtin - tackline
I agree that the instance initializer is definitely hackish, and there's no way it'd get through code review :-).
Steve
A: 

If you really cant use a field, Michael answers seems right.

Anyway. I dont know what signatures you can touch, but it seems to me that that callback is intented to do something (when the search succeeds) with/to the found object. You, instead, are intending to notify the caller of the search method that it found something. It would seems much more natural if your seach() method were made to return a boolean (the method will surely call s.onFound() somewhere if the search succeeds, then set an internal found flag there and return it).

leonbloy
This was actually the best solution in my case, even though I didn't ask as directly as I could have - I have control over this API so changing it to return a boolean makes a lot of sense. I suppose that's where my nose was pointing me but I couldn't see it on my own.
Steve
+3  A: 

You could go all functional:

Boolean found = search(new SearchCallback<Boolean>() {
    @Override Boolean onFound(Object o) { return true; }
});

Usually if you want to mutate an enclosing variable, you can express a solution more clearly by not doing so.

Tom Hawtin - tackline
Definitely the way to go if you can control the API to that level.
Yishai
While I'm a huge fan of functional programming, it doesn't make sense for onFound to return a boolean. I want the callback to be run on everything it finds, not just the first, so this assumes a particular (Boolean, Boolean) -> Boolean combinator.
Steve
@Steve If `search` is going to carry on after calling `onFound`, then that seems the wrong algorithm for the problem. You want a method that stops once the condition is met.
Tom Hawtin - tackline
No, I don't want a method that stops once the condition is met. I've clearly done a terrible job explaining myself, which is certainly helped by my poor choice of names. What if instead of `search(Callback)` I call the method `lookupMany(Map<T,V>, List<T>, Callback<Pair<T,V>>)`? Now obviously this is a silly example, but suppose my `Map.get()` is really crummy or something. In any case, my `Callback` is going to do some real work, but *incidentally* I also want to find out if `lookupMany` found *anything* and throw an error if and only if it missed on every single key.
Steve