Java 8供应商在构造函数中带有参数


81

为什么供应商仅支持无参数构造函数?

如果存在默认构造函数,则可以执行以下操作:

create(Foo::new)

但是,如果唯一的构造函数采用字符串,则必须这样做:

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

9
编译器如何猜测该参数应为“ hello”?
assylias 2015年

6
您的问题根本没有道理。你写“为什么供应商无参数的构造函数只有工作?”,然后就证明自己是一个Supplier 使用lambda表达式时工作,提供的参数,即。因此,您的实际问题似乎是“为什么仅当功能参数与目标参数匹配时方法引用才起作用”,答案是,因为这就是方法引用的目的。如果参数列表不匹配,请使用问题中已显示的lambda表达式。因为那是lambda表达的目的(不是唯一的)……
Holger 2015年

Answers:



69

但是,对于1参数的构造T,需要一个String与兼容Function<String,T>

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

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


46

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

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

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

14

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 ...”调用,以使其更加清晰。


12

为什么供应商仅与无参​​数构造函数一起使用?

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

另一方面Supplier<T>T get()它与零arg构造函数同构。

它们根本不兼容。您的create()方法需要是多态的,以接受各种功能接口,并根据提供的参数而有所不同,或者您必须编写一个lambda主体以充当两个签名之间的粘合代码。

您在这里的未满足期望是什么?什么应该在您看来怎样?


3
如果在编写时稍微强调沟通,那将是一个更好的答案。在第一句话中同时具有“同构”和“ SAM界面”似乎对于存在于一个可以帮助人们提供其不了解的内容的网站来说是一种过大的杀伤力。
L. Blanc

1

将供应商与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);
        }
    }
}

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()。

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

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

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

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

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

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


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);
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.