本文首发自「慕课网」,想理解更多 IT 干货内容,程序员圈内热闻,欢送关注!
作者 | 慕课网精英讲师 ColorfulC
本篇文章咱们将学习函数式接口相干的常识,包含什么是函数式接口,为什么须要函数式接口,如何自定义一个函数式接口,如何创立函数式接口的对象,以及一些 Java 内置的函数式接口的具体介绍等。1. 什么是函数式接口函数是接口(Functional Interface)的定义非常容易了解:只有一个形象办法的接口,就是函数式接口。能够通过 Lambda 表达式来创立函数式接口的对象。咱们来看一个在之前咱们就常常应用的 Runnable 接口,Runnable 接口就是一个函数式接口,上面的截图为 Java 源码:
咱们看到 Runnable 接口中只蕴含一个形象的 run()办法,并且在接口上标注了一个 @FuncationInterface 注解,此注解就是 Java 8 新增的注解,用来标识一个函数式接口。2. 为什么须要函数式接口学习了这么久的 Java,咱们对 Java 是纯种的面向对象的编程语言这一概念,可能有了肯定的感触,在 Java 中,所有皆是对象。然而随着 Python、scala 等语言的衰亡,函数式编程的概念失去开发者们的推崇,Java 不得不做出调整以反对更宽泛的技术要求。在面向函数编程的语言中,Lambda 表达式的类型就是函数,然而在 Java 中,Lambda 表达式的类型是对象而不是函数,他们必须依赖于一种特地的对象类型——函数式接口。所以说,Java 中的 Lambda 表达式就是一个函数式接口的对象。咱们之前应用匿名实现类示意的对象,都能够应用 Lambda 表达式来示意。3. 自定义函数式接口想要自定义一个函数式接口也非常简单,在接口上做两件事即可:定义一个形象办法:留神,接口中只能有一个形象办法;在接口上标记 @FunctionalInterface 注解:当然也能够不标记,然而如果错写了多个办法,编辑器就不能自动检测你定义的函数式接口是否有问题了,所以倡议还是写上吧。/**
- 自定义函数式接口
- @author colorful@TaleLin
*/
@FunctionalInterface
public interface FunctionalInterfaceDemo {
void run();
}
代码块 1234567891011 因为标记了 @FunctionalInterface 注解,上面接口下蕴含两个形象办法的这种谬误写法,编译器就会给出提醒:
4. 创立函数式接口对象在下面,咱们自定义了一个函数式接口,那么如何创立它的对象实例呢?咱们能够应用匿名外部类来创立该接口的对象,实例代码如下:/**
- 测试创立函数式接口对象
- @author colorful@TaleLin
*/
public class Test {
public static void main(String[] args) {
// 应用匿名外部类形式创立函数式接口
FunctionalInterfaceDemo functionalInterfaceDemo = new FunctionalInterfaceDemo() {
@Override
public void run() {System.out.println("匿名外部类形式创立函数式接口");
}
};
functionalInterfaceDemo.run();}
}
代码块 123456789101112131415161718 运行后果:匿名外部类形式创立函数式接口
代码块 1 当初,咱们学习了 Lambda 表达式,也能够应用 Lambda 表达式来创立,这种办法相较匿名外部类更加简洁,也更举荐这种做法。实例代码如下:/**
- 测试创立函数式接口对象
- @author colorful@TaleLin
*/
public class Test {
public static void main(String[] args) {
// 应用 Lambda 表达式形式创立函数式接口
FunctionalInterfaceDemo functionalInterfaceDemo = () -> System.out.println("Lambda 表达式形式创立函数式接口");
functionalInterfaceDemo.run();}
}
代码块 12345678910111213 运行后果:Lambda 表达式形式创立函数式接口
代码块 1 当然,还有一种更笨的办法,写一个接口的实现类,通过实例化实现类来创建对象。因为比较简单,而且不合乎咱们学习函数式接口的初衷,这里就不再做实例演示了。5. 内置的函数式接口介绍通过下面一系列介绍和演示,置信对于函数式接口的概念和应用,你曾经烂熟于心了。然而只晓得这些还不够用,上面的内容才是本大节的重点,Java 中内置了丰盛的函数式接口,位于 java.util.function 包下,学习这些函数式接口有助于咱们了解 Java 函数式接口的真正用处和意义。Java 内置了 4 个外围函数式接口:Comsumer<T> 消费型接口:示意承受单个输出参数但不返回后果的操作,蕴含办法:void accept(T t),能够了解为消费者,只生产(接管单个参数)、不返回(返回为 void);Supplier<T> 供应型接口:示意后果的供给者,蕴含办法 T get(),能够了解为供给者,只提供(返回 T 类型对象)、不生产(不承受参数);Function<T, R> 函数型接口:示意承受一个 T 类型参数并返回 R 类型后果的对象,蕴含办法 R apply(T t);Predicate<T> 断言型接口:确定 T 类型的对象是否满足束缚,并返回 boolean 值,蕴含办法 boolean test(T t)。咱们在 Java 的 api 文档中能够看到有一些办法的形参,会呈现下面几类接口,咱们在实例化这些接口的时候,就能够应用 Lambda 表达式的形式来实例化。咱们上面看几个实例,消费型接口应用实例:实例演示 import java.util.function.Consumer;
/**
- Java 内置 4 大外围 h 函数式接口 —— 消费型接口
- Consumer<T> void accept(T t)
- @author colorful@TaleLin
*/
public class FunctionalInterfaceDemo1 {
public static void main(String[] args) {Consumer<String> consumer = s -> System.out.println(s);
consumer.accept("只生产,不返回");
}
}
123456789101112131415 运行后果:只生产,不返回
代码块 1 供应型接口应用实例:实例演示 import java.util.function.Consumer;
import java.util.function.Supplier;
/**
- Java 内置 4 大外围 h 函数式接口 —— 供应型接口
- Supplier<T> T get()
- @author colorful@TaleLin
*/
public class FunctionalInterfaceDemo2 {
public static void main(String[] args) {Supplier<String> supplier = () -> "只返回,不生产";
String s = supplier.get();
System.out.println(s);
}
}
1234567891011121314151617 运行后果:只返回,不生产
代码块 1 上面咱们应用断言型接口,来实现一个依据给定的规定,来过滤字符串列表的办法,实例如下:实例演示 import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
/**
- Java 内置 4 大外围函数式接口 —— 断言型接口
- Predicate<T> boolean test(T t)
- @author colorful@TaleLin
*/
public class FunctionalInterfaceDemo3 {
/**
* 依据 Predicate 断言的后果,过滤 list 中的字符串
* @param list 待过滤字符串
* @param predicate 提供规定的接口实例
* @return 过滤后的列表
*/
public static List<String> filterStringList(List<String> list, Predicate<String> predicate) {
// 过滤后的字符串列表
ArrayList<String> arrayList = new ArrayList<>();
for (String string: list) {if (predicate.test(string)) {
// 如果 test 是 true,则将元素退出到过滤后的列表中
arrayList.add(string);
}
}
return arrayList;
}
public static void main(String[] args) {ArrayList<String> arrayList = new ArrayList<>();
arrayList.add("Java");
arrayList.add("PHP");
arrayList.add("Python");
arrayList.add("JavaScript");
System.out.println("过滤前:");
System.out.println(arrayList);
List<String> filterResult = filterStringList(arrayList, new Predicate<String>() {
@Override
public boolean test(String s) {
// 返回字符串中是否蕴含 P
return s.contains("P");
}
});
System.out.println("过滤后:");
System.out.println(filterResult);
}
}
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950 运行后果:过滤前:
[Java, PHP, Python, JavaScript]
过滤后:
[PHP, Python]
代码块 1234 当然,咱们学习了 Lambda 表达式,在 main()办法中就能够不再应用匿名外部类了,改写 main()办法中调用 filterStringList()办法的代码:List<String> filterResult = filterStringList(arrayList, s -> s.contains(“P”));
代码块 1 下面的实例代码可能有些难以了解,跟着我的节奏来解读一下:先定义一个办法 List<String> filterStringList(List<String> list, Predicate<String> predicate),此办法用于依据指定的规定过滤字符串列表,接管的第一个参数为待过滤列表,第二个参数是一个函数式接口类型的规定,留神,这个参数就是规定的制定者;再看 filterStringList()办法的办法体,办法体外部看待过滤列表进行了遍历,会调用 Predicate<T> 接口下的 boolean test(T t)办法,判断每一个字符串是否合乎规定,合乎规定就追加到新的列表中,最终返回一个新的过滤后的列表;在 main()办法中,咱们调用了下面定义的 filterStringList()办法,第一个参数就是待过滤列表,这里的第二个参数,是咱们创立的一个断言型接口的对象,其重写的 test(String s)办法就是过滤规定关键所在,办法体就是判断 s 字符串是否蕴含 P 字符,并一个 boolean 类型的后果;了解了第二个参数通过匿名外部类创建对象的形式,再改写成通过 Lambda 表达式的形式创建对象,就不难理解了。下面咱们介绍了外围的内置函数式接口,了解了这些接口的应用,其余接口就不难理解了。可翻阅官网文档来查看更多。6. 小结通过本篇文章学习,咱们晓得了函数式接口就是只有一个形象办法的接口,要应用 Lambda 表达式,就必须依赖函数式接口;自定义函数接口倡议应用 @FunctionalInterface 注解来进行标注,当然如果通过 Java 内置的函数式接口就能够满足咱们的需要,就不须要咱们本人自定义函数式接口了。文章最初,咱们通过一个较为简单的函数式接口实例,实现了一个过滤字符串列表的办法,如果还是不能齐全了解,倡议同学上面多加练习。
欢送关注「慕课网」,发现更多 IT 圈优质内容,分享干货常识,帮忙你成为更好的程序员!