如何使用Java Lambda的功能编程

86 阅读9分钟

使用Java Lambda的功能编程

Java是一种面向对象的编程语言。它提倡对类的所有变量和方法进行封装。也就是说,在Java版本8之前的代码块是通过绑定类的变量、方法和对象来编写的。

将一个行为传递给方法的过程需要额外的工作和负荷。这是因为我们在传递一个带有另一个属性的类。在Java 8中,引入了Lambda来表示Java的功能接口。这意味着Lambda表达式是一个函数式接口的表达。

函数式编程的引入是为了实现行为代码。这种代码使得创建一个执行特定任务的函数变得更加容易。另外,这段代码不属于任何类,因此它被当作一个值处理。

在这篇文章中,读者将了解Java的函数式编程,并学习如何将lambdas分配给一个接口。我们将使用不同数量的参数来处理lambda表达式。

读者将能够用较少的代码将对象或类的方法转换为lambdas函数。读者还将了解使用函数式编程的重要性。

前提条件

要跟上本教程,读者应具备以下条件。

  • 有Java的基本知识。
  • 安装了[Java]。
  • 自己选择的任何Java IDE。
  • 对类和静态方法的理解。

将一个函数作为一个值来传递

让我们给我们的变量分配一个值。


 String name = “foo”;

 Double pi = 3.145;

变量name 需要一个单一的字符串值foo ,变量pi 需要一个单一的双倍值3.14 。问题是,在不创建类的实例的情况下,能否将一个代码块作为值分配给一个变量名?

这在Java中是不可能的,因为只有一个对象的实例才能被分配给一个变量。让我们想象一下,一个代码块被分配给一个变量。


myfunction ablockOfCode = public void perform(){

 System. out.print(“Hello world”);

}

通过Java函数式编程,将一个代码块分配给一个变量是可能的。让我们考虑上面的代码与lambdas表达式。

为了用lambdas表达式表示上面的代码,代码块中的很多元素都可以被删除。


myfunction ablockOfCode = () -> System.out.print(“Hello world”);

从我们上面的代码中,我们可以看到访问修饰符和返回类型被删除。这是因为它们在表达式中不是必需的。

lambda表达式有一个非常简单、独特和灵活的语法。这种语法也遵循Java中向变量赋值的正常语法。也就是说,用数据类型为变量赋值,表达式为值。

Java是一种强类型的编程语言,因为每个变量都必须用一个数据类型来声明。将变量名分配给lambda表达式的方法也必须有一个类的接口的数据类型。


 myfunction ablockOfCode

作为表达式的值的语法由三部分组成,它们是:。

  1. 表达式主体 - 这可以是一个单一的表达式,也可以是一个代码块。如果函数的主体包含一个单一的表达式,那么大括号就不是必须的,这意味着它是可选的。
  2. 参数 - 这些是与接口中声明的函数的签名相匹配的功能方法。定义参数的信息种类是随意的,但参数的数量必须与接口中声明的签名相协调。
  3. 兰姆达运算符-> - 这将左边的输入参数和右边的兰姆达主体分开。

() -> System.out.print(“Hello world”)

要使用Lambda表达式,你可以创建你的功能接口或使用Java已经定义的接口。使用的接口必须与你要使用的函数的签名相匹配。一个具有单一抽象方法的接口被称为@FunctionalInterface


interface Hello {
String sayHello(String greet);
}
public class LambdaAlgorithmTest {
 public static void main(String[] args) {
 Hello hello = (message) -> {
 String str1 = "Welcome ";
 String str2 = str1 + message;
 return str2;
 };
 System.out.println(hello.sayHello("Lambda Tutorial"));
 }

}

上面代码的第一行是一个接口,名称为Hello 。该接口声明了一个名为sayHello 的抽象方法。该方法需要一个名为greet 的参数,该参数的数据类型为string ,因此其返回类型为字符串。

第四行创建了一个名为LambdaAlgorithmTest 的新类,其中包含main方法。该方法声明了一个名为hello 的函数,其接口类型为Hello

我们还有另一个方法,它需要一个参数,叫做message ,有两个变量str1str2 ,它们的类型都是string 。第一个变量str1 ,取一个值Welcome ,第二个变量str2 ,串联str1 ,参数message 。该函数返回变量str2

使用不同的Lambda参数工作

不带参数的Lambda例子


@FunctionalInterface
interface MyFunctionalInterface {
 //A method with no parameter
 String sayHello();
}
public class Example {
 public static void main(String args[]) {
 // lambda expression
 MyFunctionalInterface msg = () -> "Hello";
 System.out.println(msg.sayHello());

 }

}

上述代码的第一行,@FunctionalInterface 是一个注解,确保功能接口没有一个以上的抽象方法。第二行声明了一个接口,名称为MyFunctionalInterface

该接口声明了一个名为sayHello 的方法,该方法不接受任何参数。该方法将返回类型化的字符串。

第三行创建了一个名为Example 的新类,其中包含main方法。该方法声明了一个类型为MyFunctionalInterface 接口的函数msg 。该函数的结果将打印Hello

带有单个参数的Lambda例子


@FunctionalInterface

interface MyFunctionalInterface {
 //A method with a single parameter
 int incrementByFive(int a);
}
public class LambdaSingleParam {
 public static void main(String args[]) {
 // lambda expression with single parameter num
 MyFunctionalInterface foo = (num) -> num+5;
 System.out.println(f.incrementByFive(22));

 }
}

上述代码的第一行,@FunctionalInterface 是一个Java注解,确保功能接口没有一个以上的抽象方法。第二行是一个接口,名称为MyFunctionalInterface 。该接口声明了一个名为increaseByFive 的抽象方法,它需要一个参数。该方法将返回int类型。

第三行创建了一个名为LambdaSingleParam 的新类。该类包含主方法。主方法声明了一个类型为MyFunctionalInterface 接口的函数foo 。该函数的结果将打印27

有许多参数的Lambda例子


interface StringConcat {
 String strConcat(String a, String b);
}
public class LambdaMultipleParameter {
 public static void main(String args[]) {
 // lambda expression with many arguments
 StringConcat str = (str1, str2) -> str1 + str2;
 System.out.println("Result: "+ str.strConcat("Hello ", "World"));

 }

}

上述代码的第一行,interface StringConcat ,声明了一个新的接口StringConcat 。该接口声明了一个抽象的方法,名为strConcat 。该方法需要两个参数。该方法将返回类型化的字符串。

第三行创建了一个名为LambdaMultipleParameter 的新类。该类包含主方法,作为代码的入口。该方法声明了具有StringConcat接口类型的函数str

最后一行代码将字符串Result 与方法调用的结果str.strConcat("Hello ", "World") 连接起来。函数的结果将打印Result: Hello World

Lambda流管道

流是实现接口的类的对象stream 。流要么来自特定的流接口之一,用于准备原始数据值的集合。流使你能够用lambda对元素的集合采取行动。

Lambda Stream流水线是一个来自数据源(来自集合)的处理步骤序列。该管道执行两个主要的操作,这是一个中间或终端操作。这些操作形成一个链式方法。

中间操作和终端操作

一个中间操作是一个活动,它在一个流元素上播放一个特定的任务,任务的结果形成一个新的流。中间操作也被称为Lazy 操作,这意味着在调用终端操作之前,该操作不会被执行。

操作描述
过滤过滤方法允许我们得到满足某些条件的流元素。
分明distinct方法返回流中的不同元素。
limitlimit方法返回具有给定数量或限制的流的元素。
地图map方法允许我们将一个流的元素映射到另一个流的元素。
排序的以给定的顺序返回流中的元素。

中间操作

一个终端操作开始处理在一个流上进行的中间操作,并产生一个结果。终端操作被称为eager 操作。渴望操作是指只要被调用就会执行要求的任务的操作。

操作描述
forEach循环浏览流,对每个元素进行操作。
计数返回被执行操作的元素的总数。
平均数返回流中元素数值的平均值。
min返回流中最小的数字元素。
最大返回数据流中最大的数字元素。
forEach循环流,对每个元素进行操作。
收集创建一个新的容器,用于对即时流操作的操作。
findAny返回基于中间操作的流的任何元素。
findFirst返回符合谓词的流的第一个元素。
anyMatch检查是否有流与谓词匹配。
减少使用累加器返回流中所有元素的单一值。
allMatch检查是否所有的元素都符合指定的谓词。

流操作的例子


public static void main(String[] args) {
        List<String> myList = Arrays.asList("a1", "a2", "b1", "c2", "c1");
        myList.stream()
                .filter(s -> s.startsWith("c"))
                .map(String::toUpperCase)
                .sorted()
                .forEach(System.out::println);
    }

上面代码的第一行声明了main方法。main方法是代码进入Java解释器的入口点。代码的第二行声明了一个ArrayListmyList ,它接收一个数组值。

  • Arrays.asList() 将数组转换为一个列表。
  • myList.stream() 将列表转换为一个流。
  • filter(s -> s.startsWith("c")) 过滤列表中以 开始的元素。c
  • map(String::toUpperCase) 将符合过滤条件的元素转换为大写字母。
  • sorted() 方法默认返回升序排序的元素。
  • forEach(System.out::println) 循环浏览流中的所有元素,并打印出满足管道中指定的所有要求的元素。

Java中lambda的好处

  • Lambda表达式提高了代码的可读性,不需要解释。
  • 兰姆达允许你编写简洁的代码。
  • 它鼓励使用函数式编程。
  • 它简化了变量范围,鼓励代码的可重用性。
  • Lambdas允许你使用并行处理。

总结

在这篇文章中,我们研究了Java中函数式编程的概念。我们解释了一个函数如何被当作一个值来使用。我们还研究了如何创建一个函数式程序以及如何在lambda中使用不同的参数。

最后,我们通过对中间和终端操作的深入了解,解释了流管道的概念。文章还谈到了lambda函数式编程的好处。