Java 8功能接口
欢迎使用Java 8功能接口示例教程。
Java一直是面向对象的编程语言。
这意味着Java编程中的所有内容都围绕对象(为了简单起见,某些基本类型除外)。
我们在Java中不仅只有函数,它们是Class的一部分,我们需要使用class/object来调用任何函数。
Java 8功能接口
如果我们研究其他编程语言,例如C++,JavaScript;它们被称为函数式编程语言,因为我们可以编写函数并在需要时使用它们。
其中一些语言支持面向对象的编程以及功能编程。
面向对象还不错,但是它给程序带来了很多冗长的细节。
例如,假设我们必须创建一个Runnable实例。
通常,我们使用如下所示的匿名类来执行此操作。
Runnable r = new Runnable(){ @Override public void run() { System.out.println("My Runnable"); }};
如果看上面的代码,实际使用的部分是run()方法中的代码。
剩下的所有代码都是因为Java程序的结构方式。
Java 8功能接口和Lambda表达式通过删除许多样板代码来帮助我们编写更小巧,更简洁的代码。
Java 8功能接口
具有唯一一种抽象方法的接口称为功能接口。
添加了@FunctionalInterface注释,以便我们可以将接口标记为功能接口。
不一定要使用它,但最好的做法是将其与功能接口一起使用,以避免意外添加其他方法。
如果接口使用@FunctionalInterface注释进行注释,而我们尝试使用多个抽象方法,则会引发编译器错误。
Java 8功能接口的主要好处是我们可以使用lambda表达式实例化它们,并避免使用笨重的匿名类实现。
Java 8 Collections API已被重写,并且引入了使用许多功能接口的新Stream API。
Java 8在java.util.function
包中定义了许多功能接口。
一些有用的Java 8功能接口是"消费者","供应商","功能"和"谓词"。
您可以在Java 8 Stream Example中找到有关它们的更多详细信息。
java.lang.Runnable是单个抽象方法run()的功能接口的一个很好的例子。
下面的代码片段为功能接口提供了一些指导:
interface Foo { boolean equals(Object obj); } //Not functional because equals is already an implicit member (Object class) interface Comparator<T> { boolean equals(Object obj); int compare(T o1, T o2); } //Functional because Comparator has only one abstract non-Object method interface Foo { int m(); Object clone(); } //Not functional because method Object.clone is not public interface X { int m(Iterable<String> arg); } interface Y { int m(Iterable<String> arg); } interface Z extends X, Y {} //Functional: two methods, but they have the same signature interface X { Iterable m(Iterable<String> arg); } interface Y { Iterable<String> m(Iterable arg); } interface Z extends X, Y {} //Functional: Y.m is a subsignature & return-type-substitutable interface X { int m(Iterable<String> arg); } interface Y { int m(Iterable<Integer> arg); } interface Z extends X, Y {} //Not functional: No method has a subsignature of all abstract methods interface X { int m(Iterable<String> arg, Class c); } interface Y { int m(Iterable arg, Class<?> c); } interface Z extends X, Y {} //Not functional: No method has a subsignature of all abstract methods interface X { long m(); } interface Y { int m(); } interface Z extends X, Y {} //Compiler error: no method is return type substitutable interface Foo<T> { void m(T arg); } interface Bar<T> { void m(T arg); } interface FooBar<X, Y> extends Foo<X>, Bar<Y> {} //Compiler error: different signatures, same erasure
Lambda表达
Lambda Expression是我们可以在Java面向对象的世界中可视化函数编程的方式。
对象是Java编程语言的基础,没有对象就无法拥有函数,这就是Java语言仅支持通过函数接口使用lambda表达式的原因。
由于功能接口中只有一个抽象函数,因此在将lambda表达式应用于该方法时不会造成混淆。
Lambda Expressions语法为(参数)->(正文)。
现在让我们看看如何使用lambda表达式在匿名Runnable之上编写代码。
Runnable r1 = () -> System.out.println("My Runnable");
让我们尝试了解上面的lambda表达式中发生了什么。
Runnable是一个功能性的界面,这就是为什么我们可以使用lambda表达式创建其实例的原因。
由于run()方法没有参数,因此我们的lambda表达式也没有参数。
就像if-else块一样,我们可以避免使用花括号({}),因为方法主体中只有一条语句。
对于多个语句,我们必须像其他任何方法一样使用花括号。
为什么我们需要Lambda表达式
减少代码行
使用lambda表达式的明显好处之一是减少了代码量,我们已经看到,使用lambda表达式而不是使用匿名类可以轻松创建功能接口的实例。顺序和并行执行支持使用lambda表达式的另一个好处是,我们可以受益于Stream API的顺序和并行操作支持。
为了说明这一点,让我们举一个简单的示例,在此示例中,我们需要编写一种方法来测试传递的数字是否为质数传统上我们会像下面这样写它的代码。
代码尚未完全优化,但出于示例目的,所以请耐心等待。
上面的代码的问题在于它本质上是顺序的,如果数量很大,那么将花费大量时间。
代码的另一个问题是退出点太多,而且不可读。
让我们看看如何使用lambda表达式和流API编写相同的方法。
IntStream是一系列原始int值元素,支持顺序和并行聚合操作。
这是Stream的int原语专业化。
为了提高可读性,我们还可以像下面这样编写方法。
如果您不熟悉IntStream,则它的range()方法以1的增量步骤从startInclusive(包含)到endExclusive(不含)返回顺序的有序IntStream。
noneMatch()方法返回此流中是否没有元素与提供的谓词匹配。
如果不需要确定结果,则可能不会评估所有元素上的谓词。
- 将行为传递给方法让我们看看如何使用lambda表达式通过一个简单的例子来传递方法的行为。
假设我们必须编写一种方法来对列表中的数字进行匹配(如果它们符合给定的条件)。
我们可以使用谓词并编写如下的方法。
用法示例:
- 惰性提高效率使用lambda表达式的另一个优势是惰性求值,例如,我们需要编写一种方法来找出3到11范围内的最大奇数并返回其平方,通常我们将为这种方法是这样的:
上面的程序将始终按顺序运行,但是我们可以使用Stream API来实现这一点并从懒惰寻求中受益。
让我们看看如何使用Stream API和lambda表达式以功能编程的方式重写此代码。
如果您对双冒号(::)运算符感到惊讶,它是Java 8中引入的,可用于方法引用。
Java编译器负责将参数映射到被调用的方法。
它是Lambda表达式的简短形式,即"--isGreaterThan3(i)"或者"--NumberTest.isGreaterThan3(i)"。
Lambda表达式示例
下面,我为lambda表达式提供了一些代码片段,并附有一些小注释来解释它们。
//Traditional approach private static boolean isPrime(int number) { if(number < 2) return false; for(int i=2; i<number; i++){ if(number % i == 0) return false; } return true; }
方法和构造方法参考
方法引用用于引用方法而不调用它。
类似地,构造函数引用用于引用构造函数,而无需创建命名类或者数组类型的新实例。
方法和构造函数引用的示例:
//Declarative approach private static boolean isPrime(int number) { return number > 1 && IntStream.range(2, number).noneMatch( index -> number % index == 0); }