- 接口默认方法(Default Methods)
- 函数式接口(SAM类型的接口(Single Abstract Method))
- lambda表达式
- 方法引用
接口默认方法(Default Methods)
接口定义:
public interface FunctionInter {
/**
* 接口默认方法1 (默认方法可有多个)
*/
default String testdefaultFunction1(){
System.out.println("testdefaultFunction1");
return "testdefaultFunction1";
}
/**
* 接口默认方法2 (默认方法可有多个)
* @return
*/
default String testdefaultFunction2(){
System.out.println("testdefaultFunction2");
return "testdefaultFunction2";
}
/**
* 接口可定义静态变量
*/
public static String foo = "foo";
/**
* 接口可定义静态方法
* @return
*/
public static String testFunctionInterStatic(){
System.out.println("testFunctionInter");
return "testFunctionInterStatic";
}
/**
* 接口可定义抽象方法
*/
public abstract void testAbstract();
}
接口实现:
public class FunctionInterfaceTest implements FunctionInter {
@Override
public void testAbstract() {
System.out.println("testAbstract");
}
public static void main(String[] args) {
FunctionInterfaceTest item = new FunctionInterfaceTest();
//默认方法
item.testdefaultFunction1();
//默认方法
item.testdefaultFunction2();
//抽象方法
item.testAbstract();
//接口静态方法调用 通过接口来调用
FunctionInter.testFunctionInterStatic();// 接口静态方法调用 正确
// item.testFunctionInterStatic(); 接口静态方法调用 错误
// FunctionInterfaceTest.testFunctionInterStatic(); 接口静态方法调用 错误
//接口静态变量调用 通过实现类来调用
System.out.println(FunctionInterfaceTest.foo);
//接口静态变量调用 通过接口来调用
System.out.println(FunctionInter.foo);
}
}
- 接口定义中新增带有默认实现的方法
- 接口定义抽象方法
- 接口定义静态变量和静态实现方法
- 静态变量可被实现类引用,静态实现方法不可以被实现类引用
函数式接口
函数式接口其实本质上还是一个接口,但是它是一种特殊的接口:SAM类型的接口(Single Abstract Method)。定义了这种类型的接口,使得以其为参数的方法,可以在调用时,使用一个lambda表达式作为参数。从另一个方面说,一旦我们调用某方法,可以传入lambda表达式作为参数,则这个方法的参数类型,必定是一个函数式的接口,这个类型会使用@FunctionalInterface进行修饰,或者抽象方法只有一个的接口不用@FunctionalInterface修饰也可作为函数式接口来用。
从SAM原则上讲,这个接口中,只能有一个函数需要被实现,但是也可以有如下例外:
1. 默认方法与静态方法并不影响函数式接口的契约,可以任意使用,即
函数式接口中可以有静态方法,一个或者多个静态方法不会影响SAM接口成为函数式接口,并且静态方法可以提供方法实现
可以由 default 修饰的默认方法方法,这个关键字是Java8中新增的,为的目的就是使得某一些接口,原则上只有一个方法被实现,但是由于历史原因,不得不加入一些方法来兼容整个JDK中的API,所以就需要使用default关键字来定义这样的方法
2. 可以有 Object 中覆盖的方法,也就是 equals,toString,hashcode等方法。
JDK中以前所有的函数式接口都已经使用 @FunctionalInterface 定义,可以通过查看JDK源码来确认,以下附JDK 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
函数式接口定义方式1 (不用@FunctionalInterface修饰,但是函数式接口必须只有一个抽象待实现的方法,可以包含其他的默认的实现方法或者静态的已实现方法)
public interface FunctionInterfaceDefine {
public String functionBody();
default String defaultBody(){
return "defaultBody";
}
}
@FunctionalInterface
public interface FunctionInterfaceDefineByAnnotation {
public String functionBody();
default String defaultBody(){
return "defaultBody";
}
}
lambda表达式
一个不用被绑定到一个标识符上,并且可能被调用的函数。这个解释还不够通俗,lambda表达式可以这样定义(不精确,自己的理解):一段带有输入参数的可执行语句块
语法
(Type1 param1, Type2 param2, ..., TypeN paramN) -> {
statment1;
statment2;
//.............
return statmentM;
}
1. 参数类型省略–绝大多数情况,编译器都可以从上下文环境中推断出lambda表达式的参数类型
(param1,param2, ..., paramN) -> {
statment1;
statment2;
//.............
return statmentM;
}
2. 当lambda表达式的参数个数只有一个,可以省略小括号。lambda表达式简写为:
param1 -> {
statment1;
statment2;
//.............
return statmentM;
}
3. 当lambda表达式只包含一条语句时,可以省略大括号、return和语句结尾的分号。lambda表达式简化为:
param1 -> statment
lambda眼中的this
在lambda中,this不是指向lambda表达式产生的那个SAM(函数接口)对象,而是声明它的外部对象。
public class DoFunctionInterfaceByLambda {
public void doFunctionInterface(FunctionInterfaceDefine functionInterfaceDefine){
System.out.println(functionInterfaceDefine.functionBody());
}
public void testThis(){
DoFunctionInterfaceByLambda byLambda = this;
byLambda.doFunctionInterface(()-> {
System.out.println(this.getClass().toString()); //打印 DoFunctionInterfaceByLambda
return "doFunctionInterface" ;
} );
}
}
方法引用
用语法格式有以下三种:
- objectName::instanceMethod
- ClassName::staticMethod
- ClassName::instanceMethod
前两种方式类似,等同于把lambda表达式的参数直接当成instanceMethod|staticMethod的参数来调用。比如System.out::println等同于x->System.out.println(x);Math::max等同于(x, y)->Math.max(x,y)。
最后一种方式,等同于把lambda表达式的第一个参数当成instanceMethod的目标对象,其他剩余参数当成该方法的参数。比如String::toLowerCase等同于x->x.toLowerCase()。
构造器引用
构造器引用语法如下:ClassName::new,把lambda表达式的参数当成ClassName构造器的参数 。例如BigDecimal::new等同于x->new BigDecimal(x)。
Streams