Java8新特性1——函数式接口&lambda表达式
Java8新特性1——函数式接口&lambda表达式
注:以下内容基于Java 8,所有代码都已在Java 8环境下测试通过
目录:
- Java8新特性1——函数式接口&lambda表达式
- 方法引用
- Stream
1. 函数式接口
如果在一个接口中,有且只有一个抽象方法,则该接口被称为函数式接口。如:
interface Test {
void test();
}
注:
可以在接口前使用 @FunctionalInterface
注解,判断这个接口是否是⼀个函数式接口。如:
@FunctionalInterface
interface Test1 {//有且仅有一个抽象方法,是函数式接口
void test();
}
@FunctionalInterface
interface Test2 {//有且仅有一个抽象方法,是函数式接口
void test();
default void f() {
}
}
@FunctionalInterface
interface Test3 {//没有抽象方法,不是函数式接口,编译器报错
}
@FunctionalInterface
interface Test4 {//有多个抽象方法,不是函数式接口,编译器报错
void test1();
void test2();
}
2. lambda表达式
2.1 lambda表达式作用
lambda表达式是一个匿名函数,用于简化函数式接口的实现。
在Java中,接口不能实例化,但接口对象可以指向实现类对象。当没有实现类对象时,可以通过匿名类的方式,如:
public class Main {
public static void main(String[] args) {
Test test = new Test() {
@Override
public void f() {
System.out.println("使用匿名函数的方式实现了函数式接口");
}
};
test.f();
}
}
@FunctionalInterface
interface Test {
void f();
}
使用匿名类的方式代码不是很简洁,因此引入了lambda表达式,如:
public class Main {
public static void main(String[] args) {
Test test = () -> System.out.println("使用lambda表达式的方式实现了函数式接口");
test.f();
}
}
@FunctionalInterface
interface Test {
void f();
}
在使用lambda表达式之后,代码变得简洁了很多,因此可以说lambda表达式是和函数式接口相辅相成的。在上面的代码中,lambda表达式实际做了以下三个工作:
-
自动实现接口
Test test = new Test();
-
将
->
前的参数自动添加到抽象函数里面(上面代码中抽象函数没有参数)void f();
-
将
->
后的语句作为抽象函数的方法体void f(){ System.out.println("使用lambda表达式的方式实现了函数式接口"); }
2.2 lambda表达式语法格式
lambda表达式的格式如下:
(参数1, 参数2, ……) -> {
方法体;
}
其中:
- 参数要求和函数式接口中抽象方法的参数一致(包括数量和类型以及顺序)
- 如果函数式接口中抽象方法有返回值,则实现的时候也需要返回值
public class Main {
public static void main(String[] args) {
Test test = (int x, int y) -> {//参数、返回值与函数式接口中抽象方法一致
return x + y;
};
test.add(1, 2);
}
}
@FunctionalInterface
interface Test {
int add(int x, int y);
}
2.3 lambda表达式的精简
-
参数精简
- 参数类型可以省略,若省略一个类型参数,则所有的类型参数都要省略
- 若只有一个参数,则小括号可以省略
- 若参数为0或者多于1个,则小括号不可以省略
-
方法体精简
- 若方法体中只有一行代码,则花括号可以省略
- 若方法体中只有一行代码且是return语句,则在省略大括号的时候还需要去掉return关键字
- 若方法体中有多行代码或者使用了return语句,则大括号不可以省略
public class Main { public static void main(String[] args) { //只有一个参数,省略了小括号 //只有一条return语句,省略了花括号即return关键字 Test test = x -> Math.exp(x); test.exp(1); } } @FunctionalInterface interface Test { double exp(double x); }
2.4 变量作用域
- lambda表达式只可以访问外部变量,但不能修改外部变量
- lambda表达式访问的外部变量一般都是声明为 final 的,但也可以不用声明为 final ,但该变量在声明后不能被修改
- lambda表达式中不允许声明一个与局部变量同名的参数或局部变量
public class Main { static final int a = 0; public static void main(String[] args) { final int num1 = 10; int num2 = 20; //num2 = 40; //声明后不能被修改 Test test1 = x -> { System.out.println(num1);//可以访问外部被声明为 final 的变量 System.out.println(num2);//可以访问外部的普通变量 //num1 = 20;//只能访问,不能修改 //num2 = 20;//只能访问,不能修改 //int num1 = 20;//不允许声明一个与局部变量同名的局部变量 return Math.exp(x); }; //num2 = 40; //声明后不能被修改 test1.exp(1); //不允许声明一个与局部变量同名的参数 //Test test2 = num1 -> Math.exp(num1); } } @FunctionalInterface interface Test { double exp(double x); }
3. 四大函数式接口
为了让开发者高效地使用函数式接口,Java 8 在 java.util.function
包下提供了许多函数式接口,以下四种是最为常见的:
接口原型 | 抽象方法 | 备注 |
---|---|---|
Consumer< T > | accept(T t) | 消费型接口 |
Supplier< T > | T get() | 供给型接口 |
Function<T, R> | R apply(T t) | 函数型接口 |
Predicate< T > | boolean test(T t) | 断言型接口 |
3.1 Consumer< T >:消费型接口
该接口只接收输入参数但不输出返回值,消费对象,只进不出。
-
接口原型:
@FunctionalInterface public interface Consumer<T> { void accept(T t); }
-
使用示例:
import java.util.function.Consumer; public class Main { public static void main(String[] args) { Consumer<Integer> acc = (t) -> System.out.println(t);//实现 Consumer 接口 acc.accept(10); } }
3.2 Supplier< T >:供给型接口
该接口只输出返回值但不接收输入参数,生成对象,只出不进。
-
接口原型:
public interface Supplier<T> { T get(); }
-
使用示例:
import java.util.function.Supplier; public class Main { public static void main(String[] args) { Supplier<Integer> sup = () -> 10;//实现 Supplier 接口 System.out.println(sup.get()); } }
3.3 Function<T, R>:函数型接口
该接口既接收输入参数又输出返回值,用于指定特定功能,有进有出。
-
接口原型:
@FunctionalInterface public interface Function<T, R> { R apply(T t); }
-
使用示例:
import java.util.function.Function; public class Main { public static void main(String[] args) { Function<Integer, String> fun = (x) -> {//实现 Function 接口 String out = "输入的整数是" + x; return out; }; System.out.println(fun.apply(10)); } }
3.4 Predicate< T >:断言型接口
该接口既接收输入参数又输出返回值,且返回值只能是布尔值,用于条件判断,有进有出。
-
函数原型:
public interface Predicate<T> { boolean test(T t); }
-
使用示例:
import java.util.function.Predicate; public class Main { public static void main(String[] args) { Predicate<Integer> pre = (x) -> x % 2 == 0;//实现 Predicate 接口 int a = 10; if (pre.test(10)) { System.out.println(a + "是偶数"); } else { System.out.println(a + "是奇数"); } } }