Java 8 Supplier with arguments in the constructor
Why do suppliers only support no-arg constructors?
If the default constructor is present, I can do this:
create(Foo::new)
But if the only constructor takes a String, I have to do this:
create(() -> new Foo("hello"))
Solution 1:
But, a 1-arg constructor for T
that takes a String
is compatible with Function<String,T>
:
Function<String, Foo> fooSupplier = Foo::new;
Which constructor is selected is treated as an overload selection problem, based on the shape of the target type.
Solution 2:
That's just a limitation of the method reference syntax -- that you can't pass in any of the arguments. It's just how the syntax works.
Solution 3:
If you like method references so much, you can write a bind
method by yourself and use it:
public static <T, R> Supplier<R> bind(Function<T,R> fn, T val) {
return () -> fn.apply(val);
}
create(bind(Foo::new, "hello"));
Solution 4:
The Supplier<T>
interface represents a function with a signature of () -> T
, meaning it takes no parameters and returns something of type T
. Method references that you provide as arguments must follow that signature in order to be passed in.
If you want to create a Supplier<Foo>
that works with the constructor, you can use the general bind method that @Tagir Valeev suggests, or you make a more specialized one.
If you want a Supplier<Foo>
that always uses that "hello"
String, you could define it one of two different ways: as a method or a Supplier<Foo>
variable.
method:
static Foo makeFoo() { return new Foo("hello"); }
variable:
static Supplier<Foo> makeFoo = () -> new Foo("hello");
You can pass in the method with a method reference(create(WhateverClassItIsOn::makeFoo);
), and the variable can be passed in simply using the name create(WhateverClassItIsOn.makeFoo);
.
The method is a little bit more preferable because it is easier to use outside of the context of being passed as a method reference, and it's also able to be used in the instance that someone requires their own specialized functional interface that is also () -> T
or is () -> Foo
specifically.
If you want to use a Supplier
that can take any String as an argument, you should use something like the bind method @Tagir mentioned, bypassing the need to supply the Function
:
Supplier<Foo> makeFooFromString(String str) { return () -> new Foo(str); }
You can pass this as an argument like this: create(makeFooFromString("hello"));
Although, maybe you should change all the "make..." calls to "supply..." calls, just to make it a little clearer.