JDK1.8新特性函数式编程:lambda表达式/函数式接口/接口的默认和静态方法
接口的默认和静态方法
内容:
Java 8允许我们给接口添加一个非抽象的方法实现,只需要使用 default关键字即可,这个特征又叫做扩展方法。
解读:
首先我们来看,在JDK1.7,接口与抽象类的区别有:
- 抽象类可以有构造方法,接口中不能有构造方法。
- 抽象类中可以有普通成员变量,接口中没有普通成员变量
- 抽象类中可以包含非抽象的普通方法,接口中的所有方法必须都是抽象的,不能有非抽象的普通方法。
- 抽象类中的抽象方法的访问类型可以是public,protected和,但接口中的抽象方法只能是public类型的,并且默认即为public abstract类型。
- 抽象类中可以包含静态方法,接口中不能包含静态方法
- 抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以任意,但接口中定义的变量只能是public static final类型,并且默认即为public static final类型。
- 一个类可以实现多个接口,但只能继承一个抽象类。
其中,“抽象类中可以包含非抽象的普通方法,接口中不能有非抽象的普通方法”和“抽象类中可以包含静态方法,接口中不能包含静态方法”两条在JDK1.8之后被打破了。
JDK1.8允许我们使用关键字default创建默认方法,这个方法是被具体实现了的方法,可以有多个;静态方法也被允许创建了。
代码示例:
public interface TestInterface {
// static修饰符定义静态方法
static void staticMethod() {
System.out.println("接口中的静态方法");
}
// default修饰符定义默认方法
default void defaultMethod() {
System.out.println("接口中的默认方法");
}
}
函数式接口、lambda表达式
内容:
Lambda 表达式:
(例如: (x, y) -> { return x + y; } ;λ表达式有三部分组成:参数列表,箭头(->),以及一个表达式或语句块。)
函数式接口:
每一个lambda表达式都对应一个类型,通常是接口类型。而“函数式接口”是指仅仅只包含一个抽象方法的接口,每一个该类型的lambda表达式都会被匹配到这个抽象方法。因为 默认方法 不算抽象方法,所以你也可以给你的函数式接口添加默认方法。
们可以将lambda表达式当作任意只包含一个抽象方法的接口类型,确保你的接口一定达到这个要求,你只需要给你的接口添加 @FunctionalInterface 注解,编译器如果发现你标注了这个注解的接口有多于一个抽象方法的时候会报错的。
解读:
Lambda表达式需要“函数式接口”的支持,函数式接口是指接口中只有一个抽象方法的接口。
在我个人理解中,函数式接口更偏向于“函数”,而不是“方法”,对于这个新特性我们应该以函数式编程的思想去理解它,不再拘泥于面向对象,为什么说它是“函数”呢?因为它本身就是为了实现一种函数的功能而产生的,类似于面向过程当中的函数,只是为了解决某一问题而产生的函数,并不完全依赖于对象。
在使用lambda表达式时就可以展现的淋漓尽致,它不依赖于创建对象才能使用方法,简化创建对象的过程,直接完成方法。
代码示例:
我们来描写一个用例,让我们的函数式接口完成一个打招呼的功能。首先定义函数式接口:
package com.dyit.lambda;
/**
* 函数式接口:这个接口就相当于一个函数,只关注函数方法的实现
* @author apple
*
*/
@FunctionalInterface
public interface Person {
String jerrySay(String msg);
}
JDK1.8就没必要使用传统的匿名对象的方式了,lambda表达式提供了更简洁的写法。
lambda表达式实现和普通实现对比:
package com.dyit.lambda;
public class Tom {
public static void tomSay(Person p ) {
String saywordString = p.jerrySay("hello tom");
System.out.println(saywordString);
}
public static void main(String[] args) {
/**
* 一般写法:创建接口类,实现接口类方法
*/
Tom.tomSay(new Person() {
@Override
public String jerrySay(String msg) {
return "jerry say:"+msg;
}
});
/**
* 函数式编程:lambda表达式,
* 实际上就是创建了这个类
* 并且完成了这个函数式接口想要实现的函数内容
* (参数)—> {
* 方法体;
* return:返回值
* }
*/
Tom.tomSay((msg) -> "jerry say:"+msg);
}
}
运行结果:
lambda表达式的基本语法形式:
lambda表达式的基本语法形式:()->方法体。
而在整个java之中,对于lambda表达式一共定义了有三类语法:
(params) -> 单行语句;
(params) ->表达式;
(params) ->{多行语句};
如果在编写lambda表达式的过程之中,如果只有一条语句,需要返回值,那么不需要有return。
下面定义一个可以用于加法计算的数学接口,里面可以接收两个数据,而后进行加法计算。
interface MyMath {
public int add(int x,int y);
}
public class TestDemo {
public static void main(String[] args) {
MyMath myMath = (x,y) -> x + y;
System.out.println("计算结果:" + myMath.add(10,20));
}
}
如果说现在要在程序里面编写多行代码,那么就需要使用“{}”定义:当返回值有多行语句的时候return不能省略。
Lambda表达式于接口的默认方法:
描述:
如果此时在接口里面定义了更多的方法的时候,那么就会直接在接口上出现错误的提示。但是从jdk1.8开始,接口的定义形式又发生了改变。在jdk1.8之前,所有的接口定义的方法其权限都属于public,可是这个严格的权限要求被“半打破”。接口可以进行动态的扩充了。一个实现好的接口,现在又突然要增加新的方法,但是这个增加的方法不希望影响到子类。这种情况下就可以使用default方法。
代码示例:
@FunctionalInterface
interface Message {
public String getInfo();
default void print() {//此方法不要求子类实现
System.out.println("www.baidu.com");
}
}
public class TestDemo {
public static void main(String[] args) {
Message msg = () -> "Hello World!";
System.out.println("计算结果:" + msg.getInfo());
msg.print();
}
}