简介
函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。
函数式接口可以被隐式转换为 lambda 表达式。
JDK 1.8 新增加的函数接口,
- java.util.function(该包下新增了许多函数式接口,基本满足常用功能)
案例
举例之前先看下函数式接口简介,所以函数式接口到底什么意思呢?我们先抛开分支只讲重点,其实函数式接口指的就是只有一个抽象方法的接口,那么这类接口单独拎出来给了一个名字,就叫函数式接口。
所以这类接口以前就存在,只不过没有归类取名字,比如Runnable接口等。
为此java8这次为这类接口引入了一个新的注解@FunctionalInterface,主要用于编译级错误检查,加上该注解,当你写的接口不符合函数式接口定义的时候,编译器会报错,比如像以前就有的Runnable接口,可以进源码看看,可以发现已经补上了@FunctionalInterface。
接下来问题是,为什么这类接口要单独拎出来讲,这是因为java在函数式编程这块有缺陷,方法不能作为参数传递,只能依靠类进行曲线救国,这次的函数式接口就是为了弥补这块的不足,从命名也能看出目的。而之前有讲到的lambda也不是java独有的,其它支持函数式编程的语言早就支持lambda表达式,那么lambda表达式在java中该怎么理解,和函数式接口又是什么关系呢?接下来我们举个🌰看看
import org.junit.Test; import java.util.function.Function; /** * @author lc * @describe 功能描述 * @Date 10:54 on 2019/8/11 */ public class Java8 { public static void main(String[] args) { } @Test public void testFun(){ String testStr = "abcd"; //java8之前的写法 StrSubitFun fun1 = new StrSubitFun() { @Override public String subString(String inStr,int startIndex) { return inStr.substring(startIndex); } }; //java8lambda StrSubitFun funLambda = (String inStr, int startindex) -> inStr.substring(startindex); //java8 方法引用 StrSubitFun funReference = String::substring; System.out.println(fun1.subString(testStr,1)); System.out.println(funLambda.subString(testStr,1)); System.out.println(funReference.subString(testStr,1)); } /** * 字符串分割接口 */ interface StrSubitFun{ String subString(String inStr,int startIndex); } }
接下来再看看我们的问题:lambda表达式在java中该怎么理解,和函数式接口又是什么关系呢?
//java8lambda StrSubitFun funLambda = (String inStr, int startindex) -> inStr.substring(startindex);
从这行代码可以明显看出,lambda表达式返回值是一个实例,并且这个实例的类型是之前说过的函数式接口。
也就是说lambda其实就是这个函数式接口的实现(可以理解成new),最开始我们就知道函数式接口的特点,就是只有一个抽象方法,那么这里隐含了lambda表达式所实现的内容其实就是这一个抽象方法(可以理解成实现了这个抽象方法)。
所以综上所述,lambda表达式其实隐含了2步,实例化函数式接口,并实现了唯一的一个抽象方法。(这个只是理解,实际上还是有差异,比如内存结构等)
也就可以看出,java8弥补了函数式编程的缺乏,同时仍然是曲线救国,依然不能把方法当参数,但是在编码过程中通过lambda同样达到函数式编程的写法和效果。
最后做一个总结:
1、如果一个接口只有一个抽象方法,那么该接口就是一个函数式接口。
2、如果我们在某个接口上声明了FunctionalInterface注解,那么编译器就会按照函数式接口的定义来要求该接口。
3、如果某个接口只有一个抽象方法,但我们并没有给该接口声明FuncationalInterface注解,那么编译器依旧依旧会将该接口看作是函数式接口。(虽这样说,但是一般对于函数式接口建议还是加上这个注解的比较好。因为加了之后编译器会对接口增加一个强制性的保证,如果接口不满足某些条件的话是会报错提示的,就这好比子类重写父类的一个特定方法的时候,照理是应该在子类的这个方法上增加一个override方法,但是如果不加也没问题,加了的好处一是代码可读性比较好,二是如果覆写了父类中并不存在的方法那么编译器会第一时间提示出来,所以最好按照规则来:如果满足函数式接口一定要在接口上声明FuncationalInterface注解)