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}