使用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
作为表达式的值的语法由三部分组成,它们是:。
- 表达式主体 - 这可以是一个单一的表达式,也可以是一个代码块。如果函数的主体包含一个单一的表达式,那么大括号就不是必须的,这意味着它是可选的。
- 参数 - 这些是与接口中声明的函数的签名相匹配的功能方法。定义参数的信息种类是随意的,但参数的数量必须与接口中声明的签名相协调。
- 兰姆达运算符
->
- 这将左边的输入参数和右边的兰姆达主体分开。
() -> 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
,有两个变量str1
和str2
,它们的类型都是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方法返回流中的不同元素。 |
limit | limit方法返回具有给定数量或限制的流的元素。 |
地图 | 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函数式编程的好处。