一个絮叨的工作笔记,不想看废话的直接从 自定义@FunctionalInterface 往下看吧
起因
最近在学习spring-boot源码的时候发现了一个特殊的语法。
private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
// 获得 ServletContextInitializer 对象
return this::selfInitialize;
}
懵逼了好久,后才查询了之后才知道上面内容等价于
return new ServletContextInitializer() {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
selfInitialize(servletContext);
}
};
}
新的问题
为什么会等价于这个呢?对于程序员永远要对新的事物抱有兴趣,然后我就去找了找这个语法还有什么别的用处。
一个简单的demo
首先根据网上的说法::的作用是,左边是类,又变是类中的方法,简单的例子
public class DaiTest {
public static void main(String[] args) {
List<String> list = Arrays.asList("1","2","3","4");
// 在循环中将每个循环出来的内容,调用DaiTest.print的方法
list.forEach(DaiTest::print);
}
public static void print(String s) {
System.out.print(s);
}
}
执行的结果是
1234
java.util.function
使用IDEA的时候点击::会发现跳转到了一个新的接口上,会发现这就是函数式接口
@FunctionalInterface
public interface Consumer<T> {
/**
*
* @param t the input argument
*/
void accept(T t);
/**
*
*/
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
所以发现之前的代码
public static void main(String[] args) {
List<String> list = Arrays.asList("1","2","3","4");
// 在循环中将每个循环出来的内容,调用DaiTest.print的方法
list.forEach(DaiTest::print);
}
等价于
public static void main(String[] args) {
List<String> list = Arrays.asList("1","2","3","4");
// 在循环中将每个循环出来的内容,调用DaiTest.print的方法
Consumer<String> consumer = DaiTest::print;
list.forEach(consumer);
}
可以看出来可以根据输入参数和输出参数的不同返回不同的function方法,那么我们看看到底什么是函数式接口
@FunctionalInterface
Java 8为函数式接口引入了一个新注解@FunctionalInterface,主要用于编译级错误检查,加上该注解,当你写的接口不符合函数式接口定义的时候,编译器会报错。
@FunctionalInterface注释的约束同时也是函数式接口的约束
- 接口有且只能有个一个抽象方法,只有方法定义,没有方法体
- 在接口中覆写Object类中的public方法,不算是函数式接口的方法。
看文字实在是不知道怎么理解,那么下面我们做几个例子看看。
自定义@FunctionalInterface
首先我们有个类,有如下方法
public class DaiTest {
public String getStr(String str) {
System.out.println("接收的参数:" + str);
return "456";
}
public Boolean getBo(String str) {
return false;
}
public Integer getInt(String str) {
System.out.println("接收的参数:" + str);
return 1;
}
public Long getLong(String str) {
return 1L;
}
public Object getOb(String str) {
return new Object();
}
public DaiTest() {
System.out.println("开始创建DaiTest");
}
}
创建3个@FunctionalInterface,分别对应getStr、getInt和构造函数的入参、出参
@FunctionalInterface
public interface TestFunction {
String getfunction(String str);
}
@FunctionalInterface
public interface TestIntFunction {
Integer getfunction(String str);
}
@FunctionalInterface
public interface TestObjFunction {
DaiTest getfunction();
}
然后执行main方法中的代码
public static void main(String[] args) {
DaiTest test = new DaiTest();
TestFunction testFunction = test::getStr;
TestIntFunction getInt = test::getInt;
TestObjFunction aNew = DaiTest::new;
String testFunction1 = testFunction.getfunction("testFunction");
System.out.println("运行TestFunction结束");
Integer getInt1 = getInt.getfunction("getInt");
System.out.println("运行TestIntFunction结束");
DaiTest getfunction = aNew.getfunction();
System.out.println("运行TestObjFunction结束");
}
最后返回结果是
接收的参数:testFunction
运行TestFunction结束
接收的参数:getInt
运行TestIntFunction结束
开始创建DaiTest
运行TestObjFunction结束
可以看到我们调用接口的方法,实际上调用的是DaiTest类方法的引用
总结
- ::(双冒号)的语法, 冒号左边定义了类,右边定义了类对应的方法,指向到一个符合函数式的接口。
- 需要注意的几个,方法假如非静态类,则类需要首先实例化出来。就像正常方法调用,你不可能直接调用类的非静态化方法
- 此时你调用函数式接口的方法实际调用的逻辑是,引用的方法。
- 符合函数式的接口的定义:
- 接口有且只能有一个抽象方法。
- 接口中可以重写Object的方法,不计算到方法数量限制中。
- @FunctionalInterface不是必须的,对的@FunctionalInterface注解只是方便编译时候校验,接口只要符合函数式即使没有注解也是可以的。(不过建议能加上注解还是加上)。
- 接口的入参和出参要和引用的方法一致。
- 除了重写Object方法,在接口中通过default实现的方法也不计算到方法数量限制中。
- 目前个人这个功能接触的比较少,还不清楚哪种环境种使用这种语法会方便些 ε=(´ο`*)))唉。