杭州有实力的网站开发,成都网站建设联系电话,品优购html代码,九曲网站建设Java是一门古老的语言#xff0c;并且有很多新手在Java#xff08;JVM#xff09;领域挑战它们。 但是Java 8到来并带来了一些有趣的功能。 这些有趣的功能使编写新的惊人框架#xff08;如Spark Web框架或Javaslang#xff09;成为可能 。 在本文中#xff0c;我们将介… Java是一门古老的语言并且有很多新手在JavaJVM领域挑战它们。 但是Java 8到来并带来了一些有趣的功能。 这些有趣的功能使编写新的惊人框架如Spark Web框架或Javaslang成为可能 。 在本文中我们将介绍将函数式编程引入Java的Javaslang。 函数式编程这有什么用 如今似乎所有优秀的开发人员都希望进行一些功能编程。 因为他们以前想使用面向对象的编程。 我个人认为函数式编程可以很好地解决某些问题而其他范例则更好。 在以下情况下函数式编程非常有用 您可以将其与不变性配对纯函数没有副作用并且更容易推理。 纯函数意味着不变性从而极大地简化了测试和调试。 但是并非所有解决方案都能很好地代表不变性。 有时您只是拥有大量数据这些数据在多个用户之间共享并且您想就地进行更改。 在这种情况下可变性是解决方法。 您的代码取决于输入而不取决于状态如果某物取决于状态而不是输入那么听起来对我来说更像是一个函数。 理想情况下功能代码应非常明确地说明正在使用的信息因此仅应使用参数。 这也意味着更多通用和可重用的功能。 您具有独立的逻辑这些逻辑之间的耦合程度不高以小型通用和可重用功能组织的功能代码非常有用 您拥有要转换的数据流我认为这是最容易看到函数式编程值的地方。 实际上流在Java 8中引起了很多关注。 讨论图书馆 正如您可以在javaslang.com上阅读的那样 Java 8在我们的程序中引入了λc但是“显然JDK API不会帮助您编写简洁的功能逻辑…” – jOOQ™博客 Javaslang™是编写全面的功能性Java 8程序的缺失部分和最佳解决方案。 正如我所看到的Javaslang一样Java 8为我们提供了启用功能以构建更简洁和可组合的代码。 但是它没有做最后一步。 它打开了一个空间Javaslang到达了它。 Javaslang带来了许多功能 currying currying是功能的部分应用 模式匹配让我们将其视为函数式编程的动态调度 故障处理因为异常不利于功能组合 要么这是函数编程中非常常见的另一种结构。 典型的示例是一个函数当事情进展顺利时返回一个值而当事情进展不好时返回错误消息 元组元组是对象的一种很好的轻量级替代方案非常适合返回多个值。 只是不要偷懒并在有意义的时候使用类 备注这是功能的缓存 对于具有函数式编程经验的开发人员来说这一切都是众所周知的。 对于我们其余的人让我们看一下如何在实践中使用这些东西。 好的但是实际上我们如何使用这些东西 显然为Javaslang的每个功能显示一个示例远远超出了本文的范围。 让我们看看如何使用其中的一些尤其是让我们专注于函数式编程的基本内容函数操纵。 鉴于我沉迷于Java代码的操作我们将了解如何使用Javaslang检查某些Java代码的抽象语法树AST。 使用心爱的JavaParser可以轻松获得AST。 如果使用gradle则build.gradle文件可能如下所示 apply plugin: java
apply plugin: ideasourceCompatibility 1.8repositories {mavenCentral()
}dependencies {compile com.javaslang:javaslang:2.0.0-betacompile com.github.javaparser:javaparser-core:2.3.0testCompile junit:junit:4.12
} 我们将实现非常简单的查询。 仅查看AST即可获得解答而无需求解符号。 如果您想使用Java AST并求解符号则可能需要看一下我的这个项目 java-symbol-solver 。 例如 用给定名称的方法查找类 使用具有给定数量参数的方法查找类 查找具有给定名称的类 结合previos查询 让我们从给出CompilationUnit的函数开始方法名称返回一个TypeDeclarations列表该列表定义了使用该名称的方法。 对于从未使用过JavaParser的用户 CompilationUnit表示整个Java文件可能包含几个TypeDeclaration。 TypeDeclaration可以是类接口枚举或注释声明。 import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.body.TypeDeclaration;
import javaslang.Function1;
import javaslang.Function2;
import javaslang.collection.List;.../*** Helper method*/public static boolean hasMethodNamed(TypeDeclaration typeDeclaration, String methodName) {return List.ofAll(typeDeclaration.getMembers()).map(Match.whenType(MethodDeclaration.class).then((t)- Option.of(t.getName())).otherwise(() - Option.none())).map((n)-n.isDefined() n.get().equals(methodName)).reduce((a, b)-a || b);}public static ListTypeDeclaration getTypesWithThisMethod(CompilationUnit cu, String methodName) {return List.ofAll(cu.getTypes()).filter((t) - hasMethodNamed(t, methodName));} getTypesWithThisMethod非常简单我们在CompilationUnit cu.getTypes 中获取所有类型并对它们进行过滤仅选择具有该名称的方法的类型。 真正的工作在hasMethodNamed中完成。 在hasMethodNamed宽 E从我们的java.util.ListList.ofAlltypeDeclaration.getMembers创建一个javaslang.collection.List开始然后我们认为我们只是在MethodDeclarations感兴趣我们不是在外地感兴趣声明或类型声明中包含的其他内容因此如果方法名称与所需的methodName相匹配则将每个方法声明映射到Option.oftrue 否则将其映射到Option.offalse 。不是MethodDeclaration映射到Option.none 。 因此例如如果我们在一个具有三个字段的类中寻找一个名为“ foo”的方法然后是名为“ bar”“ foo”和“ baz”的方法我们将得到以下列表 Option.none Option.none Option.none Option.offalse Option.oftrue Option.offalse 。 下一步是将Option.none和Option.offalse都映射为false而Option.oftrue则映射为true 。 请注意我们可以立即将其连接起来而不是同时连接两个map操作。 但是我更喜欢分步做事。 一旦我们获得了一个true和false的列表我们需要从中得出一个单一值如果该列表包含至少一个true则应该为true否则为false 。 从列表中获取单个值称为减少操作。 这种操作有不同的变体我将让您详细研究:) 我们可以这样重写最新的方法 public ListTypeDeclaration getTypesWithThisMethod(CompilationUnit cu, String methodName) {Function2TypeDeclaration, String, Boolean originalFunction AstExplorer::hasMethodNamed;Function2String, TypeDeclaration, Boolean originalFunctionReversed originalFunction.reversed();Function1String, Function1TypeDeclaration, Boolean originalFunctionReversedAndCurried originalFunction.reversed().curried();Function1TypeDeclaration, Boolean originalFunctionReversedAndCurriedAndAppliedToMethodName originalFunction.reversed().curried().apply(methodName);return List.ofAll(cu.getTypes()).filter(asPredicate(originalFunctionReversedAndCurriedAndAppliedToMethodName));} 为什么我们要这样做 看起来而且确实复杂得多但是它向我们展示了如何操作函数这是获取更灵活更强大的代码的中间步骤。 因此让我们尝试了解我们在做什么。 首先快速注意一下类Function1表示一个带有一个参数的函数。 第一个泛型参数是函数接受的参数的类型而第二个泛型参数是函数返回的值的类型。 Function2取2个参数。 您可以了解这是怎么回事:) 我们 反转参数可以传递给函数的顺序 我们创建了一个部分应用的函数这是一个函数其中第一个参数是“固定的” 所以我们创建originalFunctionReversedAndCurriedAndAppliedToMethodName只运用原有的功能hasMethodNamed。 原始函数具有2个参数 TypeDeclaration和方法名称。 我们精心设计的函数仅接受TypeDeclaration。 它仍然返回一个布尔值。 然后我们可以简单地用这个微小的函数将谓词转换为函数然后可以反复使用 private static T PredicateT asPredicate(Function1T, Boolean function) {return v - function.apply(v);} 现在这就是我们可以使其更通用的方法 /** * Get all the types in a CompilationUnit which satisfies the given condition */
public ListTypeDeclaration getTypes(CompilationUnit cu, Function1TypeDeclaration, Boolean condition) {return List.ofAll(cu.getTypes()).filter(asPredicate(condition));
}/*** It returns a function which tells has if a given TypeDeclaration has a method with a given name.*/
public Function1TypeDeclaration, Boolean hasMethodWithName(String methodName) {Function2TypeDeclaration, String, Boolean originalFunction AstExplorer::hasMethodNamed;return originalFunction.reversed().curried().apply(methodName);
}/*** We could combine previous function to get this one and solve our original question.*/
public ListTypeDeclaration getTypesWithThisMethod(CompilationUnit cu, String methodName) {return getTypes(cu, hasMethodWithName(methodName));
} 好的现在我们可以泛化hasMethodWithName了 /*** This function returns true if the TypeDeclaration has at * least one method satisfying the given condition.*/public static boolean hasAtLeastOneMethodThat(TypeDeclaration typeDeclaration, Function1MethodDeclaration, Boolean condition) {return List.ofAll(typeDeclaration.getMembers()).map(Match.whenType(MethodDeclaration.class).then(m - condition.apply(m)).otherwise(false)).reduce((a, b)-a || b);}/*** We refactor this function to reuse hasAtLeastOneMethodThat*/public static boolean hasMethodWithName(TypeDeclaration typeDeclaration, String methodName) {return hasAtLeastOneMethodThat(typeDeclaration, m - m.getName().equals(methodName));} 经过一些重构我们得到以下代码 package me.tomassetti.javaast;import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.body.TypeDeclaration;
import javaslang.Function1;
import javaslang.Function2;
import javaslang.collection.List;
import javaslang.control.Match;import java.util.function.Predicate;public class AstExplorer {public static boolean hasAtLeastOneMethodThat(TypeDeclaration typeDeclaration, Function1MethodDeclaration, Boolean condition) {return hasAtLeastOneMethodThat(condition).apply(typeDeclaration);}public static Function1TypeDeclaration, Boolean hasAtLeastOneMethodThat(Function1MethodDeclaration, Boolean condition) {return t - List.ofAll(t.getMembers()).map(Match.whenType(MethodDeclaration.class).then(m - condition.apply(m)).otherwise(false)).reduce((a, b)- a || b);}public static boolean hasMethodNamed(TypeDeclaration typeDeclaration, String methodName) {return hasAtLeastOneMethodThat(typeDeclaration, m - m.getName().equals(methodName));}private static T PredicateT asPredicate(Function1T, Boolean function) {return v - function.apply(v);}public static ListTypeDeclaration typesThat(CompilationUnit cu, Function1TypeDeclaration, Boolean condition) {return List.ofAll(cu.getTypes()).filter(asPredicate(condition));}public static Function1TypeDeclaration, Boolean methodHasName(String methodName) {Function2TypeDeclaration, String, Boolean originalFunction AstExplorer::hasMethodNamed;return originalFunction.reversed().curried().apply(methodName);}public static ListTypeDeclaration typesWithThisMethod(CompilationUnit cu, String methodName) {return typesThat(cu, methodHasName(methodName));}} 现在让我们看看如何使用它 package me.tomassetti.javaast;import com.github.javaparser.JavaParser;
import com.github.javaparser.ParseException;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.body.TypeDeclaration;
import javaslang.Function1;
import javaslang.collection.List;
import org.junit.Test;import java.io.InputStream;
import static me.tomassetti.javaast.AstExplorer.*;
import static org.junit.Assert.*;public class AstExplorerTest {Testpublic void typesNamedA() throws ParseException {InputStream is AstExplorerTest.class.getResourceAsStream(/SomeJavaFile.java);CompilationUnit cu JavaParser.parse(is);Function1MethodDeclaration, Boolean isNamedBar m - m.getName().equals(bar);ListTypeDeclaration res typesThat(cu, hasAtLeastOneMethodThat(isNamedBar));assertEquals(2, res.length());assertEquals(A, res.get(0).getName());assertEquals(B, res.get(1).getName());}Testpublic void typesHavingAMethodNamedBar() throws ParseException {InputStream is AstExplorerTest.class.getResourceAsStream(/SomeJavaFile.java);CompilationUnit cu JavaParser.parse(is);Function1MethodDeclaration, Boolean isNamedBar m - m.getName().equals(bar);ListTypeDeclaration res typesThat(cu, hasAtLeastOneMethodThat(isNamedBar));assertEquals(2, res.length());assertEquals(A, res.get(0).getName());assertEquals(B, res.get(1).getName());}Testpublic void typesHavingAMethodNamedBarWhichTakesZeroParams() throws ParseException {InputStream is AstExplorerTest.class.getResourceAsStream(/SomeJavaFile.java);CompilationUnit cu JavaParser.parse(is);Function1MethodDeclaration, Boolean hasZeroParam m - m.getParameters().size() 0;Function1MethodDeclaration, Boolean isNamedBar m - m.getName().equals(bar);ListTypeDeclaration res typesThat(cu, hasAtLeastOneMethodThat(m - hasZeroParam.apply(m) isNamedBar.apply(m)));assertEquals(1, res.length());assertEquals(A, res.get(0).getName());}Testpublic void typesHavingAMethodNamedBarWhichTakesOneParam() throws ParseException {InputStream is AstExplorerTest.class.getResourceAsStream(/SomeJavaFile.java);CompilationUnit cu JavaParser.parse(is);Function1MethodDeclaration, Boolean hasOneParam m - m.getParameters().size() 1;Function1MethodDeclaration, Boolean isNamedBar m - m.getName().equals(bar);ListTypeDeclaration res typesThat(cu, hasAtLeastOneMethodThat(m - hasOneParam.apply(m) isNamedBar.apply(m)));assertEquals(1, res.length());assertEquals(B, res.get(0).getName());}} 我们在此测试中使用的源文件是以下文件 class A {void foo() { }void bar() { }
}class B {void bar(int x) { }void baz() { }
} 当然这是对Javaslang潜力的非常非常非常有限的介绍。 对于那些刚接触函数式编程的人来说我认为重要的是倾向于编写非常小的函数 这些函数可以组合和操纵以获得非常灵活和强大的代码。 当我们开始使用函数式编程时它似乎显得晦涩难懂但是如果您看一下我们编写的测试我认为它们相当清晰且具有描述性。 函数式编程是否大肆宣传 我认为对函数式编程有很多兴趣但是如果过分炒作可能会导致糟糕的设计决策。 考虑一下OOP成为新的冉冉升起的新星的时间Java设计人员一路走低迫使程序员将每个代码都放在一个类中现在我们有了带有一堆静态方法的实用程序类。 换句话说我们参加了活动并要求他们假装成为获得我们的OOP奖牌的班级。 是否有意义 我不这么认为。 强烈鼓励人们学习OOP原则可能有点极端主义。 这就是为什么如果您想学习函数式编程那么可能会想要使用像Haskell这样的仅函数式语言因为它们确实真的真的推动了您进行函数式编程。 这样您就可以学习原理并在有意义的时候使用它们。 结论 我认为函数式编程是一个功能强大的工具它可以产生非常有表现力的代码。 当然它不是解决每种问题的正确工具。 不幸的是Java 8没有对标准库中的功能编程模式提供适当的支持。 但是一些启用功能已经以该语言引入并且Javaslang使现在可以编写出色的功能代码。 我认为以后会出现更多的库也许它们将使Java保持健康和更长的寿命。 翻译自: https://www.javacodegeeks.com/2015/11/functional-programming-for-java-getting-started-with-javaslang.html