Java 8 Supplier在构造函数中包含参数

时间:2015-07-06 17:03:24

标签: java lambda functional-programming java-8 functional-interface

为什么供应商只支持no-arg构造函数?

如果存在默认构造函数,我可以这样做:

create(Foo::new)

但是如果唯一的构造函数采用String,我必须这样做:

create(() -> new Foo("hello"))

8 个答案:

答案 0 :(得分:49)

但是,T的1-arg构造函数需要StringFunction<String,T>兼容:

Function<String, Foo> fooSupplier = Foo::new;

根据目标类型的形状,选择哪个构造函数被视为重载选择问题。

答案 1 :(得分:47)

这只是方法参考语法的限制 - 您无法传递任何参数。这就是语法的工作原理。

答案 2 :(得分:37)

如果您非常喜欢方法引用,可以自己编写bind方法并使用它:

public static <T, R> Supplier<R> bind(Function<T,R> fn, T val) {
    return () -> fn.apply(val);
}

create(bind(Foo::new, "hello"));

答案 3 :(得分:10)

  

为什么供应商只使用无参数构造函数?

因为1-arg构造函数与具有1个参数和1个返回值的SAM接口同构,例如java.util.function.Function<T,R>的{​​{1}}。

另一方面,R apply(T)的{​​{1}}与零arg构造函数同构。

它们根本不兼容。您的Supplier<T>方法需要是多态的,以接受各种功能接口,并根据提供的参数采取不同的行动,或者您必须编写一个lambda主体作为两个签名之间的粘合代码。

这里你未满足的期望是什么?您认为应该发生什么?

答案 4 :(得分:9)

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.

答案 5 :(得分:1)

在寻找参数化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()被喂饱了。

此方法允许e。 G。使用多个参数,动态字符串处理,字符串常量前缀,后缀等。

从理论上讲,使用身份也应比String :: new略有优势,它将始终创建一个新字符串。

正如Jacob Zimmerman所指出的那样,更简单的参数化形式

Supplier<Foo> makeFooFromString(String str1, String str2) { 
    return () -> new Foo(str1, str2); 
}

总是可能的。在上下文中这是否有意义取决于情况。

如上所述,静态方法引用调用需要相应方法的数量和返回/参数的类型,以与使用函数(流)的方法所期望的相匹配。

答案 6 :(得分:0)

将供应商与FunctionalInterface配对。

这里是一些示例代码,我将它们放在一起,用于演示“绑定”带有Function的特定构造函数的构造函数引用,以及定义和调用“工厂”构造函数引用的不同方法。

30-36MB

答案 7 :(得分:0)

如果您有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);
    }
}