为什么供应商仅支持无参数构造函数?
如果存在默认构造函数,则可以执行以下操作:
create(Foo::new)
但是,如果唯一的构造函数采用字符串,则必须这样做:
create(() -> new Foo("hello"))
为什么供应商仅支持无参数构造函数?
如果存在默认构造函数,则可以执行以下操作:
create(Foo::new)
但是,如果唯一的构造函数采用字符串,则必须这样做:
create(() -> new Foo("hello"))
Supplier
不使用lambda表达式时工作,提供的参数,即。因此,您的实际问题似乎是“为什么仅当功能参数与目标参数匹配时方法引用才起作用”,答案是,因为这就是方法引用的目的。如果参数列表不匹配,请使用问题中已显示的lambda表达式。因为那是lambda表达的目的(不是唯一的)……
Answers:
该Supplier<T>
接口与代表的签名功能() -> T
,这意味着它不带任何参数和返回类型的东西T
。您作为参数提供的方法引用必须跟随该签名才能传递。
如果要创建一个Supplier<Foo>
可与构造函数一起使用的,则可以使用@Tagir Valeev建议的常规bind方法,或者您可以使用更专门的方法。
如果要Supplier<Foo>
始终使用该"hello"
String,则可以使用两种不同的方式之一对其进行定义:作为方法或Supplier<Foo>
变量。
方法:
static Foo makeFoo() { return new Foo("hello"); }
变量:
static Supplier<Foo> makeFoo = () -> new Foo("hello");
您可以使用方法reference(create(WhateverClassItIsOn::makeFoo);
)传入该方法,并且只需使用name即可传入该变量create(WhateverClassItIsOn.makeFoo);
。
该方法是多一点点可取,因为它更容易使用的方法引用传递的背景之外,它也能够在实例中使用的是有人需要自己专门的功能接口,这也是() -> T
或() -> Foo
专门。
如果要使用Supplier
可以将任何String用作参数的a,则应使用类似提及的bind方法@Tagir之类的方法,而无需提供Function
:
Supplier<Foo> makeFooFromString(String str) { return () -> new Foo(str); }
您可以将其作为这样的参数传递: create(makeFooFromString("hello"));
虽然,也许您应该将所有的“ make ...”调用更改为“ supply ...”调用,以使其更加清晰。
为什么供应商仅与无参数构造函数一起使用?
因为1-arg构造函数与具有1个参数和1个返回值(例如的)的SAM接口是同构java.util.function.Function<T,R>
的R apply(T)
。
另一方面Supplier<T>
,T get()
它与零arg构造函数同构。
它们根本不兼容。您的create()
方法需要是多态的,以接受各种功能接口,并根据提供的参数而有所不同,或者您必须编写一个lambda主体以充当两个签名之间的粘合代码。
您在这里的未满足期望是什么?什么应该在您看来怎样?
将供应商与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);
}
}
}
在寻找参数化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()。
此方法允许使用多个参数,动态字符串处理,字符串常量前缀,后缀等。
从理论上讲,使用身份还应该比String :: new略有优势,它将始终创建一个新字符串。
正如Jacob Zimmerman所指出的那样,更简单的参数化形式
Supplier<Foo> makeFooFromString(String str1, String str2) {
return () -> new Foo(str1, str2);
}
永远是可能的。在上下文中这是否有意义取决于情况。
同样如上所述,静态方法引用调用需要相应方法的数量和返回/参数的类型,以匹配使用函数(流)的方法所期望的方法。
如果您有一个构造函数,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);
}
}