什么是函数式接口
- 所谓的函数式接口,当然首先是一个接口,然后就是在这个接口里面只能有一个抽象方法。这种类型的接口也称为SAM接口,即Single Abstract Method interfaces。(一个抽象方法、单一抽象方法接口)需要注意的是接口的默认方法和静态方法并不影响一个接口成为函数式接口。
@FunctionlInterface注解
- Java 8为函数式接口引入了一个新注解@FunctionalInterface,主要用于编译级错误检查,加上该注解,当你写的接口不符合函数式接口定义的时候,编译器会报错。
注意:函数式接口允许定义静态方法、默认方法、Object中的方法
- JDK8之前,接口中可以定义变量和方法,变量必须是public 、static、final的
- 方法必须是public 、 abstract的
- JDK8后,允许在接口中定义static方法和default方法
public interface JDK8BeforeInteface {
//static 修饰符定义静态方法
static void staticMethod(){
System.out.println("接口中的静态方法");
}
//default 修饰符定义默认方法
default void defaultMethod(){
System.out.println("接口中的默认方法");
}
- 如果有两个接口的静态方法一模一样,并且一个实现类同时实现了这两个接口,此时并不会报错,因为JDK8只能通过接口类调用接口中的静态方法所有对编译器来说是可以区分的。
- 但是如果两个接口中定义了一模一样的默认方法的时候,并且一个实现类同时实现了这两个接口,那么必须在实现类中重写默认方法,否则编译报错。
public class JDK8BeforeIntefaceImpl implements JDK8BeforeInteface,JDK8BeforeInteface2 {
//由于JDK8BeforeInteface和JDK8BeforeInteface2中default方法一样,所以这里必须覆盖
//如果接口中的默认方法不能满足某个需求的时候 ,那么实现类可以覆盖默认方法
@Override
public void defaultMethod() {
System.out.println("接口实现类覆盖了接口中的default");
}
//实现接口后,因为默认方法不是抽象方法,所以可以不用重写,但是如果开发需要,也可以重写。
JDK中的函数式接口
JDK1.8之前已有的函数式接口有:
java.lang.Runnable
java.util.concurrent.Callable
java.security.PrivilegedAction
java.util.Comparator
java.io.FileFilter
java.nio.file.PathMatcher
java.lang.reflect.InvocationHandler
java.beans.PropertyChangeListener
java.awt.event.ActionListener
javax.swing.event.ChangeListener
JDK 1.8新增的函数接口:
java.util.function 它包含了很多的类,用支持Java的函数编程。
除了早期存在的Runnable,Comparator等函数式接口之外,jdk8中又增加了java.util.function包,
其中提供了常用的函数式接口,比如:
1.Predicate
2.Function
3.Consumer
4.Supplier
Lambda表达式概述
- lambda表达式允许你通过表达式来代替功能接口。 lambda表达式就和方法一样,它提供了一个正常的参数列表和一个使用这些参数的主体(body,可以是一个表达式或一个代码块)。
- Lambda表达式还增强了集合库。 Java SE 8添加了2个对集合数据进行批量操作的包:
- java.util.function 包以及java.util.stream 包。
- 流(stream)就如同迭代器(iterator),但附加了许多额外的功能。
- 总的来说,lambda表达式和 stream 是自Java语言添加泛型(Generics)和注解(annotation)以来最大的变化。
- Lambda表达式的类型叫做"目标类型",它是一个函数接口(定义:一个接口,如果只有一个显示声明的抽象方法,那么它就是一个函数接口。一般用@FunctionlInterface)
Lambda表达式语法格式:
基本语法:
(parameters) -> expression
或
(parameters) -> {statements;}
三部分构成
参数列表
符号 ->
函数体 : 有多个语句,可以用{} 包括, 如果需要返回值且只有一个语句,可以省略 return
//函数式接口
@FunctionalInterface
interface FunctionInterface{
//int operation();
//int operation(int a);
//int operation(int x,int y);
//int operation(int x,int y);
void operation(String str);
}
public static void main(String[] args) {
//() -> 5 不需要参数,返回值为5
/*FunctionInterface fi = () -> 5;
int i = fi.operation();
System.out.println(i);*/
// x -> 2 * x 接收一个参数(数字类型),返回其2倍的值
/*FunctionInterface fi = x -> 2 * x;
int i = fi.operation(2);
System.out.println(i);*/
//(x,y) -> x-y 接收2个参数(数字),并且返回他们的差值
/*FunctionInterface fi = (x,y) -> x-y;
int operation = fi.operation(3, 2);
System.out.println(operation);*/
//(int x,int y) -> x+y 接收2个int类型的整数,返回他们的和
/*FunctionInterface fi = (int x,int y) -> x+y;
int operation = fi.operation(3, 2);
System.out.println(operation);*/
//(String s) -> System.out.print(s) 接收一个String对象,并在控制台打印,不返回任何值
FunctionInterface fi = (String s) -> System.out.println(s);
fi.operation("你好Lambda");
}
Lambda表达式
- 无参数,无返回值
- 有参数,无返回值
- 有参数,有返回值
Lambdas和Streams
- Stream是对集合的包装,通常和lambda一起使用。使用lambdas可以支持许多操作,如map,filter,limit,count,min,max,sum,collect等等
- 重点:
- forEach、forEachRemaining
//Java 8 为Interable接口新增了一个forEach默认方法,该方法所需参数的类型是一个函数式接口,
//而Iterable接口是Collection接口的父接口,因此Collection集合可以直接调用该方法
public static void main(String[] args) {
//创建一个集合
Collection<String> c = new HashSet<>();
c.add("123");
c.add("456");
c.add("789");
//方法一
for (String string : c) {
System.out.println(string);
}
/*
* 方法二
* 当程序调用Iterable的forEach(Consumer action)遍历集合元素,程序会依次将集合元素传给Consumer accept(T)方法*该接口中唯一的抽象方法。
* 正因为Consumer是函数式接口,因此可以使用Lambda表达式来遍历集合
*/
c.forEach(new Consumer<String>() {
@Override
public void accept(String t) {
System.out.println("集合元素:"+t);
}
});
//方法三
//调用forEach()方法遍历集合
c.forEach(t -> System.out.println("迭代集合元素:"+t));
/*
* JDK8中有双冒号用法,就是把方法当作参数传到stream内部,使stream的每个元素都传入到该方法里面执行。
*
* 类名::方法名
* 注意:是方法名 ,后面没有括号"()"
*
* 例如:
* 表达式:
* person -> person.getAge()
* 可以替换成
* Person::getAge
*
* 表达式
* () -> new HashMap<>();
* 可以替换成
* HashMap::new
*/
c.forEach(System.out::println);
}
- JDK8还新增了forEachRemaining(Consumer action)方法,该方法所需要的Consumer参数同样是函数式接口。
- 当程序调用Iterator的forEachRemaining(Consumer action)遍历集合元素。
- 程序会一次将集合元素传递给Consumer的accept(T t)方法(该接口中唯一的抽象方法)
public static void main(String[] args) {
Collection<String> c = new HashSet<>();
c.add("西门庆");
c.add("武大郎");
c.add("小潘");
Iterator<String> iterator = c.iterator();
iterator.forEachRemaining(System.out::println);
}