前言
今天学习Java8 新特性,主要是之前在学习 Scala、JavaFX 中遇到一些 Lambda 表达式,感觉 lambda 表达式确实很简洁,很有必要学一学。
目录
前言
1、接口的默认方法与静态方法
编写接口
编写接口的实现类
测试
2、Lambda表达式(重点)
2.1、Lambda 表达式规范
2.1.1、函数接口的定义
2.1.2、Java 内置的函数接口
2.2、Lambda 基础语法
2.2.1、无参方法调用
(1)编写函数接口
(2)使用 lambda
2.2.2、有参方法调用
(1)编写函数接口
(2)使用 lambda
2.3、Lambda 的精简写法
2.3.1、无参方法
2.3.2、有参方法
练习
2.4、Lambda 实战
forEach
集合排序
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}