JDK1.8 新特性(一)【默认方法、静态方法和Lambda表达式】
前言
今天学习Java8 新特性,主要是之前在学习 Scala、JavaFX 中遇到一些 Lambda 表达式,感觉 lambda 表达式确实很简洁,很有必要学一学,重点是提升逼格,让别人看不懂才是最装的。
目录
1、接口的默认方法与静态方法
什么是普通方法?
我们可以把 Java 中的方法看成两类:普通方法(有方法体的)和抽象方法(没有方法体的,需要子类去实现的,比如接口、抽象类)。
JDK8 之前,Java 中接口 Interface 之中可以定义变量和方法:
- 变量 必须是 public、static、final 的
- 方法 必须是 public、abstract 的
而且这些修饰符都是默认的,也就是不需要我们写。
从JDK8 开始 支持使用 static 和 default 来修饰方法,可以写方法体,不需要子类重写被 static 和 default修饰的方法。
接口中定义的默认方法只有子类对象才能调用,而接口中定义的静态方法只有接口类才能调用。
编写接口
public interface JDK8Interface {
/*
* 默认就是 public abstract 的,不需要加修饰符
*/
void add();
/*
* JDK8 提供 默认的方法
*/
default void get(){
System.out.println("get");
}
/*
* JDK8 提供的静态方法 只能通过 接口名.方法名来 调用
*/
static void del(){
System.out.println("del");
}
}
编写接口的实现类
public class JDK8InterfaceImpl implements JDK8Interface {
@Override
public void add() {
System.out.println("add");
}
}
测试
public class Test01 {
public static void main(String[] args) {
JDK8InterfaceImpl jdk8Interface = new JDK8InterfaceImpl();
jdk8Interface.add();
jdk8Interface.get();
JDK8Interface.del();
}
}
我们可以得到这样一个结论:接口中定义的默认方法只有子类对象才能调用,而接口中定义的静态方法只有接口类才能调用。
2、Lambda表达式(重点)
什么是 Lambda 表达式?
Lanmbda 表达式是一个匿名函数,即没有函数名的函数,基于数学中的 λ 而得名。
优点:简化匿名内部类的调用,减少代码量。
缺点:不好调试
编写接口:
public interface OrderService {
void get();
}
测试:
public class Test02 {
public static void main(String[] args) {
// 1. 通过匿名内部类创建一个实现OrderService接口的子类
OrderService service = new OrderService() {
@Override
public void get() {
System.out.println("get");
}
};
service.get();
// 2. lambda 表达式 括号代表参数列表
((OrderService) ()-> System.out.println("get")).get();
// 3. 同样是lambda表达式
new Thread(()-> System.out.println("get")).start();
}
}
注意:这里的接口只能有一个抽象方法!具体原因看下面。
2.1、Lambda 表达式规范
2.1.1、函数接口的定义
Java 中使用 Lambda 表达式依赖于函数接口。我们在 new 一个对象,需要传入的参数是接口类型时,可以使用该 接口的实现类,也可以直接构造一个匿名内部类(这里我们就可以通过 lambda 表达式来简化代码,提高开发效率,主要是看起来是真的舒服)。
- 在接口当中只允许存在一个抽象方法。
- 允许使用 default 和 static 修饰的方法。
- 使用注解 @FunctionalInterface 来标记该接口为函数接口(接口中只包含一个方法的叫做函数接口),我们可以发现,如果接口中出现了超过1个的抽象方法代码就会爆红。
- 可以定义 Object 类中的方法
下面是我自定义的一个函数接口。
/**
* 函数接口 只允许包含一个抽象方法
* 4. 使用注解 @FunctionalInterface 来标注着是一个函数接口
*/
@FunctionalInterface
public interface MyFunctionInterface {
// 1. 只允许存在一个方法
void get();
// 2. 允许存在 default 和 static 修饰的方法
default void add(){
System.out.println("add");
}
static void ss(){
System.out.println("ss");
}
// 3. 可以存在 Object 类中的方法
String toString();
boolean equals(Object obj);
int hashCode();
}
2.1.2、Java 内置的函数接口
new Thread(new Runnable() {
@Override
public void run() {
// 方法体
}
}).start();
正因为i Runnable 是一个函数接口,所以我们可以这样简写:
new Thread(()->{
// 方法体
}).start();
2.2、Lambda 基础语法
语法:
() -> {}
- ():参数列表
- ->:分隔符
- {}:方法体
2.2.1、无参方法调用
(1)编写函数接口
@FunctionalInterface
public interface ParamLambdaInterface {
void get();
}
(2)使用 lambda
public class Test03 {
public static void main(String[] args) {
// 1. 匿名内部类
new ParamLambdaInterface(){
@Override
public void get() {
System.out.println("get");
}
};
// 2. lambda 表达式
ParamLambdaInterface pl = () -> {
System.out.println("get");
};
pl.get();
}
}
2.2.2、有参方法调用
(1)编写函数接口
@FunctionalInterface
public interface OrderService {
void get(String name);
}
(2)使用 lambda
public class Test04 {
public static void main(String[] args) {
// 1. 通过匿名内部类创建一个实现OrderService接口的子类
OrderService service = new OrderService() {
@Override
public void get(String name) {
System.out.println(name);
}
};
service.get("tom");
// 2. lambda 表达式 参数类型可以省略
OrderService od = (name)->{
System.out.println(name);
};
od.get("get");
// 简化
((OrderService) System.out::println).get("tom");
}
}
2.3、Lambda 的精简写法
2.3.1、无参方法
上面 2.2.1 也可以这么写:
// 一行代码的情况下 可以省去花括号
((ParamLambdaInterface) () -> System.out.println("get")).get();
2.3.2、有参方法
2.2.2 可以这么写:
如果参数只有一个可以省去括号
((OrderService) name -> System.out.println(name)).get("get");
练习
定义一个用于计算和的函数接口
@FunctionalInterface
public interface AddInterface {
int add(int a,int b);
}
测试,求 1 和 2 的和
int result = ((AddInterface) (a, b) -> a + b).add(1, 2);
2.4、Lambda 实战
forEach
定义一个存放姓名的 List :
List<String> list = new ArrayList<>();
list.add("mike");
list.add("tom");
list.add("bob");
使用 List 的 forEach 方法打印所有元素:
可以看到 forEach 的参数也是一个函数接口,而且只有一个抽象方法 accept。
// 1. 普通写法
list.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
// 2. lambda
list.forEach(name-> System.out.println(name) );
// 简化
list.forEach(System.out::println);
将输出结果全部大写输出:
// 将名字转为大写再输出
list.forEach(name-> System.out.println(name.toUpperCase(Locale.ROOT)));
集合排序
数据准备
public class User {
private String name;
private Integer age;
public User(){}
public User(String name, Integer age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
创建一个 User 集合
List<User> list = new ArrayList<>();
list.add(new User("mike",20));
list.add(new User("bob",18));
list.add(new User("tom",25));
使用 lambda 进行排序:
// 1. 普通写法
list.sort(new Comparator<User>() {
@Override
public int compare(User o1, User o2) {
return 0;
}
});
// 2. lambda 表达式
list.sort((user1,user2)-> user1.getAge()-user2.getAge()); // 升序排列
// 输出结果
list.forEach(System.out::println);
运行结果:
User{name='bob', age=18}
User{name='mike', age=20}
User{name='tom', age=25}