外贸网站设计案例,中间商可以做网站吗,抖音搜索推广首选帝搜软件平台,信息可视化网站前言
方法引用是 Java 8 提供的一种新特性#xff0c;它允许我们更简洁地传递现有方法作为参数。这项特性实际上是对 Lambda 表达式的一种补充#xff0c;通过方法引用#xff0c;我们可以直接引用现有方法#xff0c;而无需编写完整的Lambda表达式。最近在使用方法引用的…前言
方法引用是 Java 8 提供的一种新特性它允许我们更简洁地传递现有方法作为参数。这项特性实际上是对 Lambda 表达式的一种补充通过方法引用我们可以直接引用现有方法而无需编写完整的Lambda表达式。最近在使用方法引用的过程中有了一些感悟这里希望以文章的形式记录下来与大家分享。 1. 背景
最近在使用 Mybatis-plus 这个框架这个框架能在 Mybatis 的基础上减少简单 SQL 的编写直接使用Java 代码的方式拼接我们想要的 SQL我为了研究怎样才能不写 SQL 也是在尽量多地翻看 Mybatis-plus 相关文档尽量能用代码解决地绝不写 SQL。 1.1. LambdaQueryMapper
LambdaQueryMapper 是 MyBatis-Plus 提供的一种基于 Lambda 表达式的查询方式。通过使用 LambdaQueryMapper我们可以利用 Lambda 表达式来编写类型安全的、更具可读性的查询代码而无需担心手动编写 SQL 或者处理字符串连接等操作。下面是一个查询示例
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.additional.query.impl.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.service.additional.query.impl.LambdaQueryWrapper;public class UserService {private UserMapper userMapper;// 查询用户列表的方法示例public ListUser queryUserList(String username, Integer age, String email) {LambdaQueryWrapperUser queryWrapper new LambdaQueryWrapper();// 构建查询条件queryWrapper.eq(User::getUsername, username).gt(age ! null, User::getAge, age).like(email ! null, User::getEmail, email);// 执行查询ListUser userList userMapper.selectList(queryWrapper);return userList;}
}在这个查询示例中我们可以通过 User::getAge 指定 age 字段为查询条件通过 User::getEmail来指定 email 为查询条件。这种形式的 Java 代码就叫做方法引用。
1.2. 函数式接口
说到方法引用就不得不说函数式接口。在 java 中只有一个抽象方法的接口叫做函数式接口每一种方法引用类型实际上对应一个函数式接口。
例如我们比较熟悉的 Runnable 它其实就是一个函数式接口。它的定义如下
FunctionalInterface
public interface Runnable {public abstract void run();
}它就代表可以接收一个没有参数也没有返回值的方法
public class Test {public static void main(String[] args) {Test test new Test();test.runnable(test::getXxx);}public void runnable(Runnable runnable){runnable.run();}public void getXxx(){System.out.println(run);}
}同样的如果我希望接收只有一个参数且只有一个返回值的方法进来怎么做呢Java 中也提供了这样的函数式接口 FunctionT,R
FunctionalInterface
public interface FunctionT, R {R apply(T t);
}它接收一个参数 T且返回一个类型 R所以我们就可以这么用
public class Test {public static void main(String[] args) {Test test new Test();test.function(test::getXyy);}public T,R void function(FunctionT,R function){}public Integer getXyy(String s){return 1;}
}1.3. 对于 User::getAge 疑惑
通过上面的叙述我们知道只要在方法中声明函数式接口我们就可以使用方法引用的形式将函数作为方法参数传入方法中。但是我在查看 LambdaQueryWrapper 源码的过程中方法它在 eq、like 等方法中定义的参数类型为函数是接口是 SFunctionT,R
FunctionalInterface
public interface SFunctionT, R extends FunctionT, R, Serializable {
}也就是说它本质也是一个 FunctionT,R ,匹配的是一个参数以及一个返回值的方法但是 User::getAge对应的 get 方法只有返回值而没有参数这是怎么匹配上的 2. 理解
经过一番查找资料我终于理解了。下面就由小的来解释一下其中的原由。方法引用我们可以将其分成两部分以 User::getAge 为例前半部分是 User它既可以是 User 类也可以是 User 对象。后半部分是 getAge 它既可以是类方法静态方法也可以是对象方法非静态方法理论上有四种组合即
对象-类方法对象-对象方法类-类方法类-对象方法
2.1. 对象::类方法
这种形式是不被允许的会直接报错 2.2. 对象::对象方法
如果是这种其实就是直接匹配也就是说方法的类型必须和方法引用对应以 FunctionT,R 为例必须得是一个参数和一个返回值代码如下
public class Test {public static void main(String[] args) {Test test new Test();//引用为对象test.function(test::getXyy);}public T,R void function(FunctionT,R function){}public Integer getXyy(String s){return 1;}
}2.3. 类::类方法
这种和上面是一样的也是要遵循参数和返回值相对应的规则
public class Test {public static void main(String[] args) {Test test new Test();//引用变为类test.function(Test::getXyy);}public T,R void function(FunctionT,R function){}/*** 变为静态方法*/public static Integer getXyy(String s){return 1;}
}2.4. 类::对象方法
而对于类::对象方法这种形式它就有点特殊了它存在一个隐式转换我们先看代码
public class Test {public static void main(String[] args) {Test test new Test();test.function(Test::getXyy);}public T,R void function(FunctionT,R function){}/*** 无参数、一个返回值的对象方法*/public Integer getXyy(){return 1;}
}function 期待的参数类型是一个参数、一个返回值的方法然而对应的却是无参数、一个返回值的方法这是怎么回事呢
原来对于这种类型的方法引用存在一个隐式转化即Test::getXyy 等价于 (Test t) - t.getXyy()而 (Test t) - t.getXyy()代表的就是一个参数一个返回值的方法所以才能匹配 FunctionT,R函数式接口类型。
为什么它需要做这样的转换本质上是由于类是无法调用对象方法所以对于 类::对象方法这种形式的方法引用都需要进行这样的隐式转换。