Java类型描述符,与LambdaWrapper源码底层探究
年轻人在科学的进程中要有冲刺力,当你老了,就会越来越胆小——杨振宁
在java
中,由于历史原因,出现在类文件结构中的二进制名称语法与我们常用的类名不同,通常使用(正斜杠)/
替换了原本的包名间隔(句号).
例如Thread
的类名叫java.lang.Thread
,但是在class
文件格式的描述符中使用的内部格式,对Thread
类名称utf8
的引用却是:java/lang/Thread
不信我们随便打开一个class
文件
可以看到类似的描述符
那如何获取类的描述符呢?它的规则又是如何呢?
首先,基本类型描述符,都是以ASCII
字符表示,例如L 正斜杠类名;
表示对象类型,[
表示数组类型
我们可以在sun.invoke.util.Wrapper
下看到对应枚举常量
例如:
-
int
的描述符为I
-
Integer
的描述符为Ljava/lang/Integer;
-
void
的描述符为V
-
java.lang.Void
的描述符为Ljava/lang/Void;
-
Object
的描述符为Ljava/lang/Object;
-
double d[][][]
的描述符为[[[D
然后方法描述符的规则是:
(参数描述符们)返回值描述符
例如:
这样一个方法:
Object m(int i, double d, Thread t) {..}
它的描述符为
(IDLjava/lang/Thread;)Ljava/lang/Object;
而我们的java.io.PrintStream#println(java.lang.Object)
也就是我们常用的System.io.println(obj)
public void println(Object x) {
String s = String.valueOf(x);
synchronized (this) {
print(s);
newLine();
}
}
描述符为
(Ljava/lang/Object;)V
java.lang.Integer#compare
——public static int compare(int x, int y) {..}
的描述符为(II)I
现在很多人有疑问了,知道这个类型描述符的规则又如何,什么场景下用得到?
这就是LambdaWrapper
能通过Lambda
获取字段名的核心啦~
举个例子:
假设现在想通过
Serializable stringConsumer = (Serializable & Function<TestModel, String>) TestModel::getName;
这样的Lambda
获取其方法名
可以使用反射调用其writeReplace
方法
Serializable stringConsumer = (Serializable & Function<TestModel, String>) TestModel::getName;
Method writeReplace = stringConsumer.getClass().getDeclaredMethod("writeReplace");
writeReplace.setAccessible(true);
java.lang.invoke.SerializedLambda serializedLambda = (java.lang.invoke.SerializedLambda) writeReplace.invoke(stringConsumer);
获取到序列化后的Lambda
,其中包含lambda
的很多信息了
这也是Mybatis-Plus
中LambdaWrapper
底层实现原理,我也对其进行了在idea
进行Debug
时的evaluate
窗口进行了一些小优化
见:https://gitee.com/baomidou/mybatis-plus/pulls/241
参考jdk
文献:
https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html
https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.2.1
真诚点赞 诚不我欺~