Java 8功能接口与功能相对应

时间:2016-12-23 23:56:33

标签: java lambda

假设我有一个需要对字符串应用多个自定义转换的应用程序。需求将随着时间的推移而增长。以下两种方法完全相同,但我想知道从长远来看哪一种更有益。它们是一样的吗?或者,随着变换数量的增加和变化,是否会提供比另一个更多的好处?

假设我们有这些:

public static final String PL = "(";
public static final String PR = ")";
public static final String Q1 = "'";

以下是每种方法的设置和使用方法。

方法1:

@FunctionalInterface
public interface StringFunction {
   String applyFunction(String s);
}

public class StrUtils {

   public static String transform(String s, StringFunction f) {
      return f.applyFunction(s);
   }

   public static String putInQ1(String s) {
      return Q1.concat(s).concat(Q1);
   }

   public static String putInParens(String s) {
      return PL.concat(s).concat(PR);
   }

   // and so on...
}

我会这样使用:

System.out.println(StrUtils.transform("anSqlStr", StrUtils::putInQ1));
System.out.println(StrUtils.transform("sqlParams", StrUtils::putInParens));

方法2:

在这里,我使用简单的功能:

Function<String, String> putInQ1 = n -> Q1.concat(n).concat(Q1);
Function<String, String> putInParens = n -> PL.concat(n).concat(PR);
// and so on...

我会这样使用:

System.out.println(putInQ1.apply("anSqlStr");
System.out.println(putInParens.apply("sqlParams");

2 个答案:

答案 0 :(得分:1)

您概述了提供某种功能的两种方式

  1. 第一个是明确地将其作为方法提供

    public static String putInQ1(String s) {
      return Q1.concat(s).concat(Q1);
    }
    

    应该通过方法引用使用。

  2. 第二个是将其作为Function对象提供:

    Function<String, String> putInQ1 = n -> Q1.concat(n).concat(Q1);
    

    (这里,你没有说明这些实例的位置。我假设你还要创建一个包含所有这些Function实例的类(可能是public static final个字段)

  3. JBNizet提到了第三个选项:您可以直接使用这些方法,而不是通过方法引用。实际上,transform功能的目的并不完全清楚。唯一的理由就是你想在那里传递任意方法引用,但这些方法引用只是Function个对象 - 就像第二种方法一样...

    然而,从技术意义上讲,差异并不大。只是为了说明这一点:两种方法都可以简单地相互转换!该方法可以基于功能对象

    来实现
    public static String putInQ1(String s) {
      return putInQ1.apply(s);
    }
    

    可以从方法参考中创建一个函数对象:

    Function<String, String> putInQ1 = StringUtils::putInQ1;
    

    所以主要的问题可能是: 你想如何向你的图书馆用户提供这个功能?

    为此,请考虑您有输入字符串的用例,并希望将其放入(括号),并将结果放入'单引号{{1} }:

    '

    你可以看到,将这些方法中的一方合格为“更好”的方法几乎没有区别。或者&#34;更糟糕&#34;在可读性方面,除了明显的事实,即最后一个比第一个更简单,避免了不必要的String doItWithMethodReferences(String input) { String result = input; result = StrUtils.transform(result, StrUtils::putInParens); result = StrUtils.transform(result, StrUtils::putInQ1); return result; } String doItWithFunctionObjects(String input) { String result = input; result = StringFunctions.putInParens.apply(result); result = StringFunctions.putInQ1.apply(result) return result; } String doItWithMethods(String input) { String result = input; result = StrUtils.putInParens(result); result = StrUtils.putInQ1(result); return result; } 调用。

    当然,这些方法中的每一种都可以在一行中以更紧凑的方式编写。但是根据操作的数量和结构,这可能严重降低可读性,事实上,这导致了另一点:我可以想象可扩展性可能是某种东西考虑一下。想象一下,您想要创建一个单个操作,将一个字符串放入transform单引号和括号'(中。

    使用方法:

    )'

    有功能:

    public static String putInPandQ1(String s) {
      return putInQ1(putInParens(s));
    }
    

    我认为Function<String, String> putInPandQ1 = putInParens.andThen(putInQ1); 函数将是一个很好的功能,有助于组成更复杂的字符串操作。

    (但是,任意一点,不得不问你是否真的没有尝试实现模板引擎或新的特定于域的编程语言......)

    简短说明:所有这些似乎都与性能无关。无论您执行andThen还是return s0 + s1;通常都无关紧要,在少数情况下,您可以稍后更改实施 - 因为,鉴于问题中草拟的功能,决策关于使用return s0.concat(s1)+或某些concat欺骗行为正是如此:实施细节。

    另外注意,正如评论中所指出的:您可以使用StringBuilder而不是定义自己的StringFunction界面。两者都在结构上相等&#34;,但第一个是标准API的一部分。想象一下,那里已经有很多库,其中期望标准UnaryOperator<String>的方法作为参数。当您只拥有自己的UnaryOperator<String>实例时,您可能必须转换这些实例,以便您的代码可以与其他代码协作。当然,这是微不足道的,但StringFunction包中的接口经过精心挑选,以涵盖大量的应用案例,我认为当程序员不必要时,库之间的互操作性可以大大增加创建标准API中已存在的新接口。一个可以认为引入functional会使代码更容易,因为它不需要StringFunction泛型参数。但是 if 你想要这个,那么你应该简单地将iterface声明为<String>,这只是一个进一步的专业化,并将保持与其他代码的兼容性。此外,您还可以方便地继承interface StringFunction extends UnaryOperator<String> { }中的所有default方法,就像我上面提到的Function一样。

答案 1 :(得分:-1)

为什么不简单地定义方法&#39; putInWhatever(String s,String left,String right){     返回左+ s +右; }

左侧和右侧的重载变量相等。不需要复杂的功能接口和lambda