1.默认方法
在java8之前子类必须实现接口所有方法。java8之后接口可以有非抽象方法,非抽象方法前面要加上default关键字,子类可以不用实现非抽象方法,也可以实现,子类根据自己需求选择实现或者不实现。接口示例如下:
public interface Add{
int add(int i); //抽象方法
default long add(long i){ //非抽象方法
return i++;
}
}
为什么要这样设计,假设java8之前Add接口是这样的:
public interface Add{
int add(int i);
}
现在java8要给Add接口加一个新的功能long add(long i),新的接口如下:
public interface Add{
int add(int i);
long add(long i);
}
那么我们java8之前写的代码就要改,比如我们用java6写的代码用到了Add接口,java6的代码用java8编译时就有问题,java8的Add接口中还有long add(long i)这个抽象方法,按以前的规则,就必须在旧代码中实现long add(long i)这个抽象方法,这样改动就太大了,所以java8给Add接口中新加的方法前面加上default(表示子类默认实现了这个方法),子类中就可以选择实现或者不实现,这样旧代码就不用改了。
2.函数式接口
函数式接口定义:只有一个抽象方法的接口(可以还有其他方法),下面两种接口写法都是函数式接口,都只有一个抽象方法int add(int i);
public interface Add {
int add(int i);
}
public interface Add{
int add(int i);
default long add(long i){
return i++;
}
}
函数式接口可以显示加上@FunctionalInterface注解,也可以不加。
@FunctionalInterface
public interface Add{
int add(int i);
default long add(long i){
return i++;
}
}
@FunctionalInterface只能加在函数式接口上,如果给非函数式接口加上会报错,如下所示:接口中有两个抽象方法,就不是函数式接口,加上@FunctionalInterface就报错。
函数式接口的作用:
java8之后内置了很多函数式接口,比如:
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
以前我们写一个Thread,是这样写的
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("hello");
}
});
thread.start();
现在可以这样写,直接写run()方法里面的代码就行:
Thread thread = new Thread(
()->System.out.println("hello")
);
thread.start();
()->System.out.println(“hello”),这个写法就是lambda表达式。
因为new Thread(Runnable r)里面的参数是Runnable,而Runnable是函数式接口,只有一个抽象方法run(),所以()->System.out.println(“hello”)这样写法默认实现了这个函数式接口run()。也可以这样写:
Runnable r = ()->System.out.println("hello");
Thread thread = new Thread(r);
thread.start();
多个函数式接口怎么办
这里可以深入思考一下,如果Thread还有一个构造方法,里面也是函数式接口,比如Thread(Other o)。那么下面这种写法,调用的是Thread(Other o)还是Thread(Runnable r)?
Thread thread = new Thread(
()->System.out.println("hello")
);
以下面为示例,定义两个函数式接口Add和Sub
public interface Add{
int add(int i);
}
public interface Sub {
int sub(int i);
}
两个函数式接口,这样写一个compute方法是没问题的,compute((i)->i)默认用的是Add接口。
public void compute(Add add){
System.out.println("add");
}
@Test
public void test(){
compute((i)->i);
}
如果再定义一个compute(Sub sub),compute((i)->i)就会报错,因为(i)->i,都满足Add和Sub中的函数式接口,
可以像下面这像写,明确指定用哪个接口
compute((Sub)(i)->i);
也可以这样写:
Sub s = (i)->i;
compute(s);
3.lambda表达式
lambda表达式基本语法是:
1.(parameters)-> expression
parameters是参数,可以为空,expression就是执行一条语句。示例:
() -> {} //空的,什么也不执行
(String str) -> System.out.println(str) //输出str
() -> "hello" //返回hello
() -> return "hello" //返回hello,和上面效果一样
(int i,int j) -> i+j // 返回i+j
2. (parameters) -> {statements;}
注意statements后面有分号,有分号就是代码块,即使只有一条语句也是代码块,要加上{}。
parameters是参数,可以为空,statements是多条语句,就是一段代码块,示例:
(int i) -> {
i++;
if(i>0){
}else {
}
return "";
}
下面这是错误写法:
() -> return "hello";
因为后面加了分号,就是代码块,要加上{},
改成这样 () -> {return “hello”;} ,或者去掉分号() -> return “hello”
总结一下,如果一个方法的参数是函数式接口,那么我们可以写一个lambda表达式传给这个方法。
下面看一个完整示例, 体会一下函数式接口和lambda的功能:
public interface NumberFilter {
boolean filter(Integer i);
}
public List<Integer> numberFiler(List<Integer> list,NumberFilter filter){
List<Integer> resultList = new ArrayList<>();
for(Integer i:list){
if(filter.filter(i)){
resultList.add(i);
}
}
return resultList;
}
@Test
public void myNumberFilterTest(){
List<Integer> list = Arrays.asList(1,2,3,4,5);
NumberFilter a = (Integer i) -> i%2==0; //单语句lambda表达式,过滤出偶数
System.out.println(numberFiler(list,a)); 输出[2,4]
NumberFilter b = (Integer i) -> { //语句块lambda表达式,过滤出奇数
if(i%2==0){
return false;
}else {
return true;
}
};
System.out.println(numberFiler(list,b)); 输出[1,3,5]
}