Java 8 供应商在构造函数中带有参数
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/31251629/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Java 8 Supplier with arguments in the constructor
提问by cahen
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"))
采纳答案by Louis Wasserman
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.
这只是方法引用语法的一个限制——您不能传入任何参数。这就是语法的工作原理。
回答by the8472
Why do suppliers only work with no-arg constructors?
为什么供应商只使用无参数构造函数?
Because a 1-arg constructor is isomorphic to a SAM interface with 1 argument and 1 return value, such as java.util.function.Function<T,R>
's R apply(T)
.
因为 1-arg 构造函数与具有 1 个参数和 1 个返回值的 SAM 接口同构,例如java.util.function.Function<T,R>
's R apply(T)
。
On the other hand Supplier<T>
's T get()
is isomorphic to a zero arg constructor.
另一方面Supplier<T>
'sT get()
与零参数构造函数同构。
They are simply not compatible. Either your create()
method needs to be polymorphic to accept various functional interfaces and act differently depending on which arguments are supplied or you have to write a lambda body to act as glue code between the two signatures.
它们根本不兼容。要么您的create()
方法需要是多态的以接受各种功能接口并根据提供的参数采取不同的行动,要么您必须编写一个 lambda 主体来充当两个签名之间的粘合代码。
What is your unmet expectation here? What shouldhappen in your opinion?
您在这里未满足的期望是什么?你认为应该发生什么?
回答by Brian Goetz
But, a 1-arg constructor for T
that takes a String
is compatible with Function<String,T>
:
但是,对于1参数的构造T
,需要一个String
与兼容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.
根据目标类型的形状,选择哪个构造函数被视为重载选择问题。
回答by Tagir Valeev
If you like method references so much, you can write a bind
method by yourself and use it:
如果你这么喜欢方法引用,你可以bind
自己写一个方法并使用它:
public static <T, R> Supplier<R> bind(Function<T,R> fn, T val) {
return () -> fn.apply(val);
}
create(bind(Foo::new, "hello"));
回答by Jacob Zimmerman
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.
该Supplier<T>
接口表示一个签名为 的函数() -> T
,这意味着它不接受任何参数并返回类型为 的东西T
。您作为参数提供的方法引用必须遵循该签名才能传入。
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.
如果你想创建一个Supplier<Foo>
与构造函数一起工作的方法,你可以使用@Tagir Valeev 建议的通用绑定方法,或者你制作一个更专业的方法。
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.
如果您想要Supplier<Foo>
始终使用该"hello"
字符串的 a,您可以将其定义为两种不同方式之一:作为方法或Supplier<Foo>
变量。
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);
.
您可以使用方法引用( create(WhateverClassItIsOn::makeFoo);
) 传入方法,只需使用名称即可传入变量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.
该方法更可取,因为它更容易在作为方法引用传递的上下文之外使用,并且它也可以用于某人需要自己的专用功能接口的情况,该接口也是() -> T
或() -> Foo
专门用于.
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
可以接受任何字符串作为参数的 ,你应该使用类似 @Tagir 提到的绑定方法的东西,绕过提供 的需要Function
:
Supplier<Foo> makeFooFromString(String str) { return () -> new Foo(str); }
You can pass this as an argument like this: create(makeFooFromString("hello"));
您可以将其作为参数传递,如下所示: create(makeFooFromString("hello"));
Although, maybe you should change all the "make..." calls to "supply..." calls, just to make it a little clearer.
虽然,也许您应该将所有“make...”调用更改为“supply...”调用,只是为了使其更清晰一些。
回答by Nathan Niesen
Pair the Supplier with a FunctionalInterface.
将供应商与 FunctionalInterface 配对。
Here's some sample code I put together to demonstrate "binding" a constructor reference to a specific constructor with Function and also different ways of defining and invoking the "factory" constructor references.
下面是一些示例代码,我放在一起来演示将构造函数引用“绑定”到具有 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);
}
}
}
回答by fozzybear
When looking for a solution to the parametrized Supplier
problem, I found the above answers helpful and applied the suggestions:
在寻找参数化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);
}
It is invoked like this:
它是这样调用的:
failedMessageSupplier(String::new, msgPrefix, customMsg);
Not quite satisfied yet with the abundant static function parameter, I dug further and with Function.identity(), I came to the following result:
对丰富的静态函数参数还不是很满意,我进一步挖掘并使用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);
};
Invocation now without the static function parameter:
现在不带静态函数参数的调用:
failedMessageSupplier(msgPrefix, customMsg)
Since Function.identity()
returns a function of the type Object
, and so does the subsequent call of apply(msgString)
, a cast to String
is required - or whatever the type, apply() is being fed with.
由于Function.identity()
返回类型为 的函数,Object
随后的 调用也是如此,因此需要apply(msgString)
强制转换String
为 - 或任何类型, apply() 被提供。
This method allows for e. g. using multiple parameters, dynamic string processing, string constants prefixes, suffixes and so on.
该方法允许例如使用多个参数、动态字符串处理、字符串常量前缀、后缀等。
Using identity should theoretically also have a slight edge over String::new, which will always create a new string.
使用标识理论上也应该比 String::new 略有优势,后者总是会创建一个新字符串。
As Jacob Zimmerman already pointed out, the simpler parametrized form
正如雅各布齐默曼已经指出的那样,更简单的参数化形式
Supplier<Foo> makeFooFromString(String str1, String str2) {
return () -> new Foo(str1, str2);
}
is always possible. Whether or not this makes sense in a context, depends.
总是可能的。这在上下文中是否有意义,取决于。
As also described above, static Method reference calls require the corresponding method's number and type of return / parameters to match the ones expected by the function-consuming (stream) method.
如上所述,静态方法引用调用需要相应方法的数量和返回/参数类型,以匹配函数消耗(流)方法所期望的。
回答by Ghilteras
If you have a constructor for new Klass(ConstructorObject)
then you can use Function<ConstructorObject, Klass>
like this:
如果你有一个构造函数,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);
}
}