网站首页 > 基础教程 正文
介绍
本文说的是函数式编程的基础知识。将从非常基础的内容开始,本系列文章的主要目的是让新手快速上手。但我确信我们还将讨论大多数有经验的 Java 程序员并没有真正意识到的东西。我们还将研究一些很酷的函数式编程方法,以应对使用 Java 集合处理数据时常见的编程挑战。
函数式编程到底是什么?
说法很多。我们都知道什么是面向对象编程,对于 OOP,我们只是使用对象 - 我们将它们分配给变量,并将它们作为参数或参数发送到 java 方法或从 java 方法接收它们。 函数式编程也是如此。我们将函数分配给变量,然后发送或接收函数。
对函数式编程的支持使 Java 更加流行。它现在是一种混合语言,因为它支持面向对象和函数式编程
但是什么是Lambda 表达式,怎么使用以及和函数之间的关系是什么?
Lambda表达式
Java 函数式编程的核心是 Lambda 表达式。 它们是 Java 函数式编程的推动者。事实上,当我们提到函数式编程时,本质上就是Lambda 表达式
Lambda表达式本身就是对象。我们编写的所有 lambda 表达式都是称为 函数接口 的特殊接口的具体实现。
现在的问题是这些功能接口是什么?函数式接口是具有某些特征的特殊接口。
函数式接口及其特点
函数式接口是 Lambda 表达式的基础。以下是函数式接口的特征。
- 函数式接口 必须只有一个抽象方法。这就是为什么它们也被称为SAM(单一抽象方法)接口。
- 函数式接口可以有多个非抽象方法,但只能有一个单个抽象方法,并继承到所有实现类。这很奇怪。我们知道Java接口不能有非抽象方法。但为什么这个说法是矛盾的呢?Java 8 在接口中引入了Default关键字,可以在接口中定义方法的默认实现。
- 函数式接口还可以有static方法
上面说的就是下面的意思:
功能接口必须有一个抽象方法,并且可以有许多可选的static 或default 方法定义。
现在我们已经了解了函数式接口的特征,让我们看几个接口并确定它们是否是函数式接口
示例1
下面是函数式接口吗?
interface SampleInterface {
}
不,它不是函数式接口。因为函数式接口必须有一个单一的抽象方法。
示例2
interface SampleInterface {
void foo(int f);
}
是的。因为它只有一个抽象方法。
示例3
interface SampleInterface {
void foo();
void bar(int b);
}
它不是。记住第一点。函数式接口必须只有一个抽象方法。但这里我们有两个抽象方法。
示例4
interface SampleInterface {
void foo();
static void bar() {
System.out.println("Static");
}
}
是的。包含一个抽象方法和一个静态方法定义。
示例5
interface SampleInterface {
void foo();
static void bar();
}
无法编译。因为静态方法必须有方法体。
示例6
interface SampleInterface {
void foo();
static void bar() {
System.out.println("Static");
}
default void bar2() {
System.out.println("Default");
}
}
是的。它有一个抽象方法、一个静态方法定义和一个默认方法。
示例7
interface SampleInterface {
void foo();
static void sbar1() {
System.out.println("Static Bar 1");
}
static void sbar2() {
System.out.println("Static Bar 2");
}
default void dbar1() {
System.out.println("Default Bar 1");
}
default void dbar2() {
System.out.println("Default Bar 1");
}
}
是的。除了单个抽象方法之外,函数式接口还可以有许多默认方法和抽象方法。
顾名思义,函数式接口首先只是接口。这意味着我们可以从中实现具体的类。上面示例7 中定义的接口的实现如下所示。
实现1:具体实现类
class SampleImpl implements SampleInterface {
@Override
public void foo() {
System.out.println("实现!!");
}
}
可以创建一个这样的对象
SampleInterface si = new SampleImpl();
si.foo();
或者,我们可以创建一个匿名内部类并重写方法 foo ,而不是创建另一个类,如下所示。
实现2:匿名内部类
SampleInterface si2 = new SampleInterface() {
@Override
public void foo() {
System.out.println("匿名内部类");
}
};
si2.foo();
我们知道重写的继承特性。如果我们想创建另一个实现,我们可以简单地通过复制粘贴部分代码来实现它。这就是接口的全部意义,是吗?
但你看到这里的问题了吗?我将其称为复制粘贴编程。如果我们必须提供数百个这样的实现,我们的项目有太多的实现类。
但如果你仔细观察的话,只有方法中的逻辑发生了变化,包含有很多的重复代码,我们所需要的只是一行打印语句。但我们最终只是将其放入样板代码中使其变得臃肿庞大。
这是面向对象编程的主要缺点。我们必须告诉编译器或运行时它必须执行的每一步。
用Lambda解决问题
现在我们了解了这个问题,我们将看看如何使用 Lambda 表达式来解决它。
很简单。使用 lambda 表达式,我们将优化上面的重复代码,只保留方法体,如下所示,并在方法参数后面嵌入一个数组运算符。
没有参数的时候
在我们的示例中,我们没有任何方法参数,因此参数是一个()
SampleInterface si = () -> {
System.out.println("hello world");
};
si.foo();
我们需要做的就是放置一个箭头运算符 (->),告诉编译器这是一个 lambda 表达式。如果只有一个语句,我们甚至可以省略方法体的大括号。如下所示。
SampleInterface si = () -> System.out.println("hello world");
si.foo();
有参数的时候
让我们看一下带有方法参数的另一个版本。假设下面是我们的功能接口定义。
interface SampleInterface {
void foo(int param);
}
匿名内部类实现
SampleInterface si2 = new SampleInterface() {
@Override
public void foo(int param) {
System.out.println("param = " + param);
}
};
si2.foo(100);
要转换上述匿名类,只需遵循与上述相同的过程。如下所示。
SampleInterface si2 = (int f) -> System.out.println("F = " + f);
甚至可以忽略数据类型关键字int。编译器将通过查看接口中的方法原型自动推断数据类型。所以更精致的会是这样的。
SampleInterface si2 = (param) -> System.out.println("param = " + param);
当我们只有一个参数时,我们甚至可以忽略括号。否则,参数是强制性的。
SampleInterface si2 = param -> System.out.println("param = " + param);
si2.foo(100);
赋值运算符 f -> System.out.println(“F = “ + f) 的右侧称为 lambda 表达式。它也称为函数表达式或简称为函数。它是功能接口SampleInterface的具体实现。
处理返回类型
现在如何处理返回类型。到目前为止,我们的抽象方法返回 void。假设我们的抽象方法有一个返回类型,如下所示。
interface Adder {
int add(int n1, int n2);
}
首先让我们从匿名内部类开始,然后将其转换为 lambda 表达式。
Adder adder = new Adder() {
@Override
public int add(int n1, int n2) {
return n1 + n2;
}
}
转换:
Adder adder = (int n1, int n2) -> return n1 + n2;
现在我们甚至不需要指定数据类型,因为编译器会推断它们。
Adder adder = (n1, n2) -> { return n1 + n2;}
这里括号是强制性的,因为我们的 lambda 表达式中有多个参数。
如果 lambda 表达式中只有一行,我们甚至可以省略 return 关键字(如下所示)和大括号。
Adder adder = (n1, n2) -> n1 + n2;
但如果我们必须使用 return 关键字,则必须使用大括号。否则,会导致编译时错误。例如,下面的代码会导致编译时错误。
Adder adder = (n1, n2) -> return n1 + n2;
还有一种情况。如果 lambda 表达式主体中有多个语句,那么我们需要将它们括在一对大括号中,如下所示。
Adder adder = (n1, n2) -> {
int sum = n1 + n2;
System.out.println(n1 + n2);
return n1 + n2;
}
这就是本文的全部内容。在下一篇文章中,我们将深入探讨几种函数式接口及其在各种情况下对应的 lambda 表达式
总结
- 与面向对象编程一样,我们将对象分配给变量,并将它们发送到方法或从方法接收它们,在函数式编程中,我们将函数分配给变量,并将它们作为实参/参数发送到方法或从方法接收它们。这里所说的函数,是指函数表达式。
- Lambda 表达式支持 Java 中的函数式编程。
- 函数式接口充当 lambda 表达式的契约。它们指定 lambda 表达式将接受哪些参数以及返回值是什么。
- 通过 lambda 表达式,所有实现 SAM 接口的匿名内部类都可以转换为 lambda 表达式。
- 在编写 lambda 表达式时,如果有多个方法参数,我们需要将它们括在括号中。如果我们有多个语句,我们必须将它们括在一对花括号中。如果我们必须包含 return 语句,则无论主体中的语句数量如何,都必须将其括在一对花括号中。
猜你喜欢
- 2024-11-30 Java开发「函数式编程」——Lambda表达式
- 2024-11-30 玩转java8 lambda表达式三之对象方法引用
- 2024-11-30 Java 中的 Supplier:让数据生成更灵活
- 2024-11-30 Java 中的 Predicate:让判断逻辑更清晰灵活
- 2024-11-30 你知道实现一个JAVA接口有几种方式吗?配合lambda更加意想不到
- 2024-11-30 java8精华-函数式编程-Predicate(四)
- 2024-11-30 巧妙应用 Java 的 Functional Interface:让代码更简洁、更灵活
- 2024-11-30 函数式接口编程真没那么难,简单几行让你的代码更优雅
- 2024-11-30 初窥函数式接口,不会取标题,没有噱头,全是干货
- 2024-11-30 JDK8新特性:函数式接口@FunctionalInterface
- 最近发表
- 标签列表
-
- gitpush (61)
- pythonif (68)
- location.href (57)
- tail-f (57)
- pythonifelse (59)
- deletesql (62)
- c++模板 (62)
- css3动画 (57)
- c#event (59)
- linuxgzip (68)
- 字符串连接 (73)
- nginx配置文件详解 (61)
- html标签 (69)
- c++初始化列表 (64)
- exec命令 (59)
- canvasfilltext (58)
- mysqlinnodbmyisam区别 (63)
- arraylistadd (66)
- node教程 (59)
- console.table (62)
- c++time_t (58)
- phpcookie (58)
- mysqldatesub函数 (63)
- window10java环境变量设置 (66)
- c++虚函数和纯虚函数的区别 (66)