2

I'm looking to replace the need for separate stand-alone static initialer functions with lambdas. e.g. I'd like to replace something like this...

class Foo {
    private static final Set<String> keywords = keywords();

    private static Set<String> keywords() {
        HashSet<String> s = new HashSet<>();
        s.add("AND");
        s.add("NOT");
        s.add("OR");
        return Collections.unmodifiableSet(s);
    }
}

With something that invokes a lambda that is defined in place at class load time. Please note, it's not my goal to invoke this lazily.

For the moment, I have created a simple Initializer class with a static method that accepts a Supplier, calls it and returns the value.

Initializer class

public class Initializer {
    public static <T> T init(Supplier<T> initializer) {
        return initializer.get();
    }    
}

Then in another class:

import static com.whatever.Initializer.init;

class Foo {
    private static final Set<String> keywords = init(() -> {
        HashSet<String> s = new HashSet<>();
        s.add("AND");
        s.add("NOT");
        s.add("OR");
        return Collections.unmodifiableSet(s);
    });
}

Is there something that exists in the standard Java libraries already so that I don't need to provide my own Initializer class, or is there some way to simply define and then execute a lambda in-place?

BillRobertson42
  • 12,602
  • 4
  • 40
  • 57
  • 1
    You don't need a method to initialize `keywords`. What exactly is the point of your lambda version? – a better oliver Oct 31 '15 at 14:43
  • 1
    Why exactly do you need lambdas here? Seems like you just need a static initializer block ( `static { }` ) and call your inits there? – Zhedar Oct 31 '15 at 14:45
  • Related: [How to initialize a map using a lambda?](http://stackoverflow.com/questions/32868665/how-to-initialize-a-map-using-a-lambda) – Didier L Nov 02 '15 at 17:43

3 Answers3

2

You can just cast the lambda and call it:

private static final Set<String> KEYWORDS = ((Supplier<Set<String>>) () -> {
    Set<String> result = new HashSet<>();
    ...
    return Collections.unmodifiableSet(result);
}).get();

Or you could use existing shortcuts:

private static final Set<String> KEYWORDS = 
    Collections.unmodifiableSet(new HashSet<>(Arrays.asList("AND", "NOT", "OR")));

But your original code, calling a method, is very readable. I wouldn't change it.

JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • 2
    For this particular case among others, I love Guava collections: `KEYWORDS = ImmutableSet.of("AND", "NOT", "OR")` – JB Nizet Oct 31 '15 at 14:57
  • I still don’t get the benefit of this lambda usage over an ordinary static initializer. In fact, it doesn’t even differ from the question’s original approach as the code is still inside a static method, now a synthetic one. Only the way it gets called has more overhead here. – Holger Nov 02 '15 at 09:14
  • @Holger I agree 100%. I just wanted to show it was possible. But as my answer say, I find the original code more readable, and wouldn't change it. – JB Nizet Nov 02 '15 at 13:27
  • Right, I understood that and wasn’t addressing you in the first place. I placed the comment under your answer instead of the question because your answer has been *accepted* so the OP’s saying it solved his actual problem but it’s still unclear in which way (or what the actual problem was)… – Holger Nov 02 '15 at 13:31
  • 2
    @JBNizet You might be interested in [JEP 269 - Convenience Factory Methods for Collections](http://openjdk.java.net/jeps/269) for Java 9! – mkobit Nov 02 '15 at 14:37
2

I would write just special utility method which does the whole chain:

public class Sets {
    public static <T> Set<T> of(T... elements) {
        return Collections.unmodifiableSet(new HashSet<>(Arrays.asList(elements)));
    }
}

And use it where necessary:

private static final Set<String> KEYWORDS = Sets.of("AND", "NOT", "OR");

If you happens to use Guava, there are already a bunch of such methods. Hopefully such feature will appear in Java-9. There's JEP 269 which would allow to write Set.of("AND", "NOT", "OR").

Tagir Valeev
  • 97,161
  • 19
  • 222
  • 334
  • While that's useful for collections, it's not for other sorts of initialization. – BillRobertson42 Nov 01 '15 at 04:44
  • 1
    @Bill, if "other sort of initialization" (whatever it is) cannot be performed in single expression, probably it should be refactored instead. Consider creating new constructors, factory methods or implement builder pattern. I believe it's much better to solve this problem for every case than create generalized solution which is ugly anyways. – Tagir Valeev Nov 01 '15 at 06:45
1

As mentioned by @zeroflagL you do not need to use lambdas in this place. You could initialize keywords in one line by using:

Collections.unmodifiableSet(new HashSet<String>(Arrays.asList("AND", "NOT", "OR")));

But this is not so pretty. Otherwise, what is wrong by using the static block? It was created for such things.

But if you do not want to use it, I think there is no standard method. You have to use your Initializer.

Fabian Damken
  • 1,477
  • 1
  • 15
  • 24