视频学习地址:Java8新特性lambda&stream&optional实现原理(余胜军通俗易懂版本)_哔哩哔哩_bilibili
1. 接口中默认方法修饰为普通方法
在jdk8之前,interface之中可以定义变量和方法,变量必须是public、static、final的,方法必须是public、abstract的,由于这些修饰符都是默认的。
在JDK 1.8开始 支持使用static和default 修饰 可以写方法体,不需要子类重写
package com.fan.interfaceTest;
public interface InterfaceJKD8 {
void test();
/**
* 默认方法 可以写方法体
*/
default void getDefaultOrder() {
System.out.println("我是默认方法 我可以写方法体");
}
static void getStaticOrder() {
System.out.println("我是静态的方法 可以写方法体");
}
}
package com.fan.interfaceTest.impl;
import com.fan.interfaceTest.InterfaceJKD8;
public class InterfaceJKD8Impl implements InterfaceJKD8 {
/**
* 默认和静态方法不是我们的抽象方法 ,所以不需要重写
*/
@Override
public void test() {
System.out.println("我是抽象方法");
}
}
2. Lambda表达式
Lambda 表达式(lambda expression)是一个匿名函数,简化我们调用匿名函数的过程。
package com.fan;
import com.fan.interfaceTest.InterfaceJKD8;
import com.fan.interfaceTest.impl.InterfaceJKD8Impl;
public class Main {
public static void main(String[] args) {
// 1.使用new的实现类的形式调用接口
InterfaceJKD8 interfaceJKD8 = new InterfaceJKD8Impl();
interfaceJKD8.test();
// // 2.使用匿名内部接口调用
new InterfaceJKD8(){
@Override
public void test() {
System.out.println("使用匿名内部接口调用");
}
};
// 3.使用lambda调用接口
InterfaceJKD8 interfaceJKD81 = () -> System.out.println("使用lambda调用接口");
interfaceJKD81.test();
// 或
((InterfaceJKD8) () -> System.out.println("使用lambda调用接口")).test();
new Thread( () -> System.out.println(Thread.currentThread().getName()+"run")).start();
}
}
3. Lambda表达式的规范
使用Lambda表达式 依赖于函数接口
- 在接口中只能够允许有一个抽象方法
- 在函数接口中定义object类中方法
- 使用默认或者静态方法
- @FunctionalInterface 表示该接口为函数接口
package com.fan.interfaceTest;
@FunctionalInterface
public interface InterfaceJKD8 {
void test();
/**
* 默认方法 可以写方法体
*/
default void getDefaultOrder() {
System.out.println("我是默认方法 我可以写方法体");
}
static void getStaticOrder() {
System.out.println("我是静态的方法 可以写方法体");
}
// Object的父类方法,可以在函数接口中重写
String toString();
}
4. Java系统内置那些函数接口
(1)消费型接口:
Conusmer<T>
void accept(T t);
BiConusmer<T,U>
void accept(T t,U u);//增加一种入参类型
(2)供给型接口
Supplier<T>
void get();
(3)函数型接口
Function<T ,R>
R apply(T t);
UnaryOperator<T>
T apply(T t);//入参与返回值类型一致
BiFunction <T ,U,R>
R apply(T t,U u);//增加一个参数类型
BinaryOperator<T>
T apply(T t1,T t2);//l两个相同类型入参与同类型返回值
ToIntFunction<T>//限定返回int
ToLongFunction<T>//限定返回long
ToDoubleFunction<T>//限定返回double
IntFunction<R>//限定入参int,返回泛型R
LongFunction<R>//限定入参long,返回泛型R
DoubleFunction<R>//限定入参double,返回泛型R
(4)断言型接口
Predicate<T>
boolean test(T t);
5. Lambda基础语法
有参数和返回值的函数接口
package com.fan.interfaceTest;
@FunctionalInterface
public interface InterfaceJDK8P {
// 有参抽象方法
int get (int i,int y);
}
基础用法
package com.fan;
import com.fan.interfaceTest.InterfaceJDK8P;
import com.fan.interfaceTest.InterfaceJKD8;
public class Main {
public static void main(String[] args) {
// 调用无参构造方法
InterfaceJKD8 interfaceJKD8 = () -> System.out.println("我是无参构造方法");
interfaceJKD8.test();
// 调用有参构造方法,并有返回值
InterfaceJDK8P interfaceJKD8P = (i, j) -> {return i+j;};
System.out.println( interfaceJKD8P.get(1,2));
// 精简版
InterfaceJDK8P interfaceJDK8P2 = (x,y) -> x+y;
System.out.println(interfaceJDK8P2.get(2,3));
// 超级精简版
((InterfaceJDK8P) (x,y) -> x+y).get(5,6);
}
}
集合的使用:
创建一个用户对象
package com.fan.bean;
public class UserEntity {
private String name;
private Integer age;
public UserEntity(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public Integer getAge() {
return age;
}
@Override
public String toString() {
return "UserEntity{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
List<String> arraylist = new ArrayList<>();
arraylist.add("a");
arraylist.add("b");
arraylist.add("c");
// 简单forEach使用
arraylist.forEach(s -> {
System.out.println(s);
});
// 按对象属性排序
List<UserEntity> userEntityList = new ArrayList<>();
userEntityList.sort((o1,o2) -> o1.getAge() - o2.getAge());
6. 方法引入
什么是方法引入
方法引用提供了非常有用的语法,可以直接引用已有的java类或对象的方法或构造器。需要结合lambda表达式能够让代码变得更加精简。
方法引入
- 静态方法引入:类名::(静态)方法名称
- 对象方法引入 类名:: 实例方法名称
- 实例方法引入 new对象 对象实例::方法引入
- 构造函数引入 类名::new
需要遵循一个规范:
方法引入的方法参数列表、返回类型与函数接口参数列表要和返回类型必须要保持一致。
方法引入实际就是lambda表达式中直接引入的方法。
类型 | 语法 | 对应lambda表达式 |
构造器引用 | Class::new | (args) -> new 类名(args) |
静态方法引用 | Class::static_method | (args) -> 类名.static_method(args) |
对象方法引用 | Class::method | (inst,args) -> 类名.method(args) |
实例方法引用 | instance::method | (args) -> instance.method(args) |
概念性的东西不好理解,上案例!
(1)静态方法引入
InterfaceJKD8
@FunctionalInterface
public interface InterfaceJKD8 {
void test();
/**
* 默认方法 可以写方法体
*/
default void getDefaultOrder() {
System.out.println("我是默认方法 我可以写方法体");
}
static void getStaticOrder() {
System.out.println("我是InterfaceJKD8的静态的方法 可以写方法体");
}
// Object的父类方法,可以在函数接口中重写
String toString();
}
public class Main {
public static void main(String[] args) {
// 1. 使用匿名内部类调用 get 方法
new InterfaceJKD8(){
@Override
public void test() {
// getStaticOrder 静态方法
InterfaceJKD8.getStaticOrder();
System.out.println("使用匿名内部类重新 get 方法,调用getStaticOrder静态方法");
}
}.test();
// lambda 表达式调用 静态方法
((InterfaceJKD8) () -> InterfaceJKD8.getStaticOrder()).test();
/**
* 使用方法引入调用方法 必须满足:
* 方法引入的方法必须和函数接口中的方法参数列表、返回值一定保持一致。
*/
InterfaceJKD8 interfaceJKD81 = Main ::getStaticMethod;
interfaceJKD81.test();
}
// 定义静态方法
public static void getStaticMethod() {
System.out.println("我是 getMethod");
}
}
我是静态的方法 可以写方法体
使用匿名内部类重新 get 方法,调用getStaticOrder静态方法
我是静态的方法 可以写方法体
我是 getMethod
可以看到,所谓的引入静态方法,实现的作用就是使用函数接口,引入一个我们自己重写的静态方法,但是这个静态方法的参数、返回值要与 函数接口的抽象方法一直。没有返回值不会报错。
(2)实例方法引入
创建实例函数接口
@FunctionalInterface
public interface shili {
String shili();
}
public class Main {
public static void main(String[] args) {
// 创建当前类的实例
Main main = new Main();
// 以当前类的实例去引入 objGet方法
ShiLi shiLi = main ::objGet;
System.out.println(shiLi.getShiLi());
}
// 定义方法
public String objGet() {
return "好家伙";
}
}
到这里感觉所谓的方法引入本质就是:通过创建一个自定义的函数接口,接口中定义一个方法,这个函数接口作为其他类方法的调用者,所以这里要保证函数接口的方法和其他类需要被调用的方法一致,即 参数类型、返回类型一致。
(3)构造函数引入
记得给 UserEntity 类添加无参构造函数哦
package com.fan.bean;
public class UserEntity {
private String name;
private Integer age;
public UserEntity() {
System.out.println("无参构造函数");
}
public UserEntity(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setAge(Integer age) {
this.age = age;
}
public Integer getAge() {
return age;
}
@Override
public String toString() {
return "UserEntity{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object obj) {
if(obj instanceof UserEntity){
return name.equals(((UserEntity) obj).name) && age == ((UserEntity) obj).age;
}else {
return false;
}
}
@Override
public int hashCode(){
return name.hashCode();
}
}
定义函数接口 UserInterface
@FunctionalInterface
public interface UserInterface {
UserEntity getUser();
}
/**
* 构造函数引入
* 函数接口返回类型::new
*/
UserInterface userInterface = UserEntity :: new;
userInterface.getUser();
输出:
无参构造函数
(4)对象方法引入
自定义函数接口,这里参数为 类对象
@FunctionalInterface
public interface MyObject {
String getString(Main main);
}
对象引入
public class Main {
public static void main(String[] args) {
/**
* 对象方法引入
* 主要实现原理就是 定义函数接口的抽象方法的参数 为 类对象,
* 调用的时候就相当于类对象.方法名
*/
// 相当于
MyObject myObject = main -> main.getString();
// 对象引入,精简写法
MyObject myObject2 = Main::getString;
System.out.println(myObject2.getString(new Main()));
}
public String getString() {
return "真6";
}
}
不得不说,真6,贯穿整个lambda表达式,核心思想就是: 先定义类型,再做具体实现。
即 先定义好入参、返回值类型,再去实现具体怎么返回的逻辑。
这里先定义好 函数接口的 入参为 Main类型,但是返回类型为 String。
再使用 lambda表达式引入,Main :: getString,即为:Main为入参类型,引入 getString方法。然后返回给 函数接口对象,通过函数接口对象调用方法。,因为已经传递好了是 Main类的getString方法,所以调用 Main类的方法。
没学过lambda我唯唯诺诺,现在我重拳出击!(O(∩_∩)O)