【问题标题】:Java 8 Supplier with arguments in the constructor构造函数中带有参数的 Java 8 供应商
【发布时间】:2015-09-23 22:35:16
【问题描述】:

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

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

create(Foo::new)

但如果唯一的构造函数接受一个字符串,我必须这样做:

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

【问题讨论】:

  • 编译器怎么可能猜到参数应该是“hello”?
  • 你的问题根本没有意义。你写了“为什么供应商只使用无参数构造函数?”,然后你证明自己 Supplier 确实 使用提供的参数,即使用 lambda 表达式时。因此,您的实际问题似乎是“为什么方法引用只有在功能参数与目标参数匹配时才有效”,答案是,因为这就是方法引用的用途。如果参数列表不匹配,请使用问题中已经显示的 lambda 表达式。因为这就是 lambda 表达式的用途(不只是)……

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


【解决方案1】:

但是,采用 StringT 的 1-arg 构造函数与 Function<String,T> 兼容:

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

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

【讨论】:

    【解决方案2】:

    这只是方法引用语法的一个限制——你不能传入任何参数。这就是语法的工作原理。

    【讨论】:

      【解决方案3】:

      如果你这么喜欢方法引用,你可以自己写一个bind方法并使用它:

      public static <T, R> Supplier<R> bind(Function<T,R> fn, T val) {
          return () -> fn.apply(val);
      }
      
      create(bind(Foo::new, "hello"));
      

      【讨论】:

        【解决方案4】:

        Supplier&lt;T&gt; 接口代表一个签名为() -&gt; T 的函数,这意味着它不接受任何参数并返回T 类型的内容。您作为参数提供的方法引用必须遵循该签名才能被传入。

        如果您想创建一个与构造函数一起使用的Supplier&lt;Foo&gt;,您可以使用@Tagir Valeev 建议的通用绑定方法,或者您制作更专业的方法。

        如果您想要一个始终使用该"hello" 字符串的Supplier&lt;Foo&gt;,您可以将其定义为两种不同的方式之一:作为方法或Supplier&lt;Foo&gt; 变量。

        方法:

        static Foo makeFoo() { return new Foo("hello"); }
        

        变量:

        static Supplier<Foo> makeFoo = () -> new Foo("hello");
        

        您可以使用方法引用(create(WhateverClassItIsOn::makeFoo);)传入方法,并且可以简单地使用名称create(WhateverClassItIsOn.makeFoo); 传入变量。

        该方法更可取一点,因为它更容易在作为方法引用传递的上下文之外使用,并且它也可以在某人需要自己的专用功能接口的情况下使用,这也是() -&gt; T 或具体为() -&gt; Foo

        如果您想使用可以将任何字符串作为参数的Supplier,您应该使用类似于@Tagir 提到的绑定方法,绕过提供Function 的需要:

        Supplier<Foo> makeFooFromString(String str) { return () -> new Foo(str); }
        

        您可以将其作为参数传递,如下所示:create(makeFooFromString("hello"));

        尽管如此,也许您应该将所有“make...”调用更改为“supply...”调用,以使其更清晰一些。

        【讨论】:

          【解决方案5】:

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

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

          另一方面,Supplier&lt;T&gt;T get() 与零参数构造函数同构。

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

          您在这里有什么未满足的期望?在您看来应该发生什么?

          【讨论】:

          • 这将是一个更好的答案,如果它被写得更强调沟通。在第一句话中同时包含“同构”和“SAM 界面”对于一个为帮助人们解决他们不理解的事情而存在的网站来说似乎有点矫枉过正。
          【解决方案6】:

          在寻找参数化 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); 
          }
          

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

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

          【讨论】:

            【解决方案7】:

            将供应商与功能接口配对。

            下面是我整理的一些示例代码,用于演示使用 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);
                    }
                }
            }
            

            【讨论】:

              【解决方案8】:

              如果您有new Klass(ConstructorObject) 的构造函数,那么您可以像这样使用Function&lt;ConstructorObject, Klass&gt;

              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);
                  }
              }
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2019-03-02
                • 2011-11-29
                • 1970-01-01
                • 1970-01-01
                • 2014-09-09
                相关资源
                最近更新 更多