为什么供应商只支持无参构造函数?
如果有默认构造函数,我可以这样做:
create(Foo::new)
但是如果唯一的构造函数接受一个字符串参数,我必须这样做:
create(() -> new Foo("hello"))
为什么供应商只支持无参构造函数?
如果有默认构造函数,我可以这样做:
create(Foo::new)
但是如果唯一的构造函数接受一个字符串参数,我必须这样做:
create(() -> new Foo("hello"))
T
类1个参数的构造函数与Function<String,T>
兼容:Function<String, Foo> fooSupplier = Foo::new;
这只是方法引用语法的限制,即您无法传递任何参数。 这就是语法的工作方式。
bind
方法的具体实现请自行完成。
public static <T, R> Supplier<R> bind(Function<T,R> fn, T val) {
return () -> fn.apply(val);
}
create(bind(Foo::new, "hello"));
Supplier<Foo>
接口表示一个带有() -> T
签名的函数,这意味着它不需要任何参数并返回类型为T
的内容。您提供作为参数的方法引用必须遵循该签名才能被传递进去。Supplier<Foo>
,您可以使用@Tagir Valeev建议的通用绑定方法,或者制作一个更加专业化的方法。"hello"
字符串的Supplier<Foo>
,您可以用两种不同的方式来定义它:作为一个方法或者一个Supplier<Foo>
变量。static Foo makeFoo() { return new Foo("hello"); }
变量:
static Supplier<Foo> makeFoo = () -> new Foo("hello");
create(WhateverClassItIsOn::makeFoo);
)来传递方法,变量可以通过名称简单地传递create(WhateverClassItIsOn.makeFoo);
。() -> T
或() -> Foo
。Supplier
,您应该使用像@Tagir提到的绑定方法,从而无需提供Function
。Supplier<Foo> makeFooFromString(String str) { return () -> new Foo(str); }
create(makeFooFromString("hello"));
尽管如此,也许您应该将所有的“make…”调用更改为“supply…”调用,以使其更清晰一些。当寻找解决参数化Supplier
问题的方案时,我发现上述答案很有帮助,并应用了建议:
private static <T, R> Supplier<String> failedMessageSupplier(Function<String,String> fn, String msgPrefix, String ... customMessages) {
final String msgString = new StringBuilder(msgPrefix).append(" - ").append(String.join("\n", customMessages)).toString();
return () -> fn.apply(msgString);
}
它的调用方式如下:
failedMessageSupplier(String::new, msgPrefix, customMsg);
对于丰富的静态函数参数,我还不太满意,因此我继续深入研究,并使用Function.identity(),得出了以下结果:
private final static Supplier<String> failedMessageSupplier(final String msgPrefix, final String ... customMessages) {
final String msgString = new StringBuilder(msgPrefix).append(" - ").append(String.join("\n", customMessages)).toString();
return () -> (String)Function.identity().apply(msgString);
};
调用现在不需要静态函数参数:
failedMessageSupplier(msgPrefix, customMsg)
Function.identity()
返回类型为Object
的函数,而后续调用apply(msgString)
也是如此,因此需要进行String
转换 - 或者无论apply()
被输入的类型是什么。
该方法允许使用多个参数、动态字符串处理、字符串常量前缀、后缀等。
使用identity理论上也比String::new
稍微快一些,因为它总是会创建一个新的字符串。
正如Jacob Zimmerman所指出的,简单的参数化形式
Supplier<Foo> makeFooFromString(String str1, String str2) {
return () -> new Foo(str1, str2);
}
总是有可能的。但这是否在某个背景下有意义,则取决于情境。
正如上面所述,静态方法引用调用需要相应方法的返回/参数数量和类型与函数消费(流)方法所期望的匹配。
将供应商与FunctionalInterface配对。
这里是我编写的一些示例代码,用于演示使用Function将构造函数引用“绑定”到特定构造函数以及定义和调用“工厂”构造函数引用的不同方法。
import java.io.Serializable;
import java.util.Date;
import org.junit.Test;
public class FunctionalInterfaceConstructor {
@Test
public void testVarFactory() throws Exception {
DateVar dateVar = makeVar("D", "Date", DateVar::new);
dateVar.setValue(new Date());
System.out.println(dateVar);
DateVar dateTypedVar = makeTypedVar("D", "Date", new Date(), DateVar::new);
System.out.println(dateTypedVar);
TypedVarFactory<Date, DateVar> dateTypedFactory = DateVar::new;
System.out.println(dateTypedFactory.apply("D", "Date", new Date()));
BooleanVar booleanVar = makeVar("B", "Boolean", BooleanVar::new);
booleanVar.setValue(true);
System.out.println(booleanVar);
BooleanVar booleanTypedVar = makeTypedVar("B", "Boolean", true, BooleanVar::new);
System.out.println(booleanTypedVar);
TypedVarFactory<Boolean, BooleanVar> booleanTypedFactory = BooleanVar::new;
System.out.println(booleanTypedFactory.apply("B", "Boolean", true));
}
private <V extends Var<T>, T extends Serializable> V makeVar(final String name, final String displayName,
final VarFactory<V> varFactory) {
V var = varFactory.apply(name, displayName);
return var;
}
private <V extends Var<T>, T extends Serializable> V makeTypedVar(final String name, final String displayName, final T value,
final TypedVarFactory<T, V> varFactory) {
V var = varFactory.apply(name, displayName, value);
return var;
}
@FunctionalInterface
static interface VarFactory<R> {
// Don't need type variables for name and displayName because they are always String
R apply(String name, String displayName);
}
@FunctionalInterface
static interface TypedVarFactory<T extends Serializable, R extends Var<T>> {
R apply(String name, String displayName, T value);
}
static class Var<T extends Serializable> {
private String name;
private String displayName;
private T value;
public Var(final String name, final String displayName) {
this.name = name;
this.displayName = displayName;
}
public Var(final String name, final String displayName, final T value) {
this(name, displayName);
this.value = value;
}
public void setValue(final T value) {
this.value = value;
}
@Override
public String toString() {
return String.format("%s[name=%s, displayName=%s, value=%s]", getClass().getSimpleName(), this.name, this.displayName,
this.value);
}
}
static class DateVar extends Var<Date> {
public DateVar(final String name, final String displayName) {
super(name, displayName);
}
public DateVar(final String name, final String displayName, final Date value) {
super(name, displayName, value);
}
}
static class BooleanVar extends Var<Boolean> {
public BooleanVar(final String name, final String displayName) {
super(name, displayName);
}
public BooleanVar(final String name, final String displayName, final Boolean value) {
super(name, displayName, value);
}
}
}
new Klass(ConstructorObject)
的构造函数,那么您可以像这样使用 Function<ConstructorObject, Klass>
:interface Interface {
static Klass createKlass(Function<Map<String,Integer>, Klass> func, Map<String, Integer> input) {
return func.apply(input);
}
}
class Klass {
private Integer integer;
Klass(Map<String, Integer> map) {
this.integer = map.get("integer");
}
public static void main(String[] args) {
Map<String, Integer> input = new HashMap<>();
input.put("integer", 1);
Klass klazz = Interface.createKlass(Klass::new, input);
System.out.println(klazz.integer);
}
}
Supplier
确实可以使用提供的参数,即当使用lambda表达式时。所以你的实际问题似乎是“为什么方法引用仅在函数参数与目标参数匹配时才有效”,答案是,因为这就是方法引用的作用。如果参数列表不匹配,请使用lambda表达式,就像你已经在你的问题中展示的那样。因为这就是lambda表达式的作用(不是唯一的作用)... - Holger