[Java] 自己写程序,来解析字段的 descriptor

背景

Java 如何理解 class 文件中字段的 descriptor? 一文中,我们已经介绍了 class \text{class} class 文件中字段的 descriptor \text{descriptor} descriptor 的结构,一些示例值如下

  • I
  • Ljava/lang/Object;
  • [I
  • [[[java/util/List;

我们能否通过解析这个 descriptor \text{descriptor} descriptor 来得到原始的类型信息呢?即类似下表这样的效果 ⬇️

descriptor \text{descriptor} descriptor ➡️ 原始的类型信息
I int
Ljava/lang/Object; java.lang.Object
[I int[]
[[[Ljava/util/List; java.util.List[][][]

先明确一个问题,因为字段的 descriptor \text{descriptor} descriptor 中不包含泛型中的类型信息(例如 List<Integer> 中的 Integer 不会保存在字段的 descriptor \text{descriptor} descriptor 里),所以我们无法通过字段的 descriptor \text{descriptor} descriptor找到泛型中的类型信息(但是可以找到 java.util.List 这个类型自身)。根据字段的 descriptor \text{descriptor} descriptor 的构造规则(细节可以参考 Java 如何理解 class 文件中字段的 descriptor? 一文),看起来可以从字段的 descriptor \text{descriptor} descriptor 中找到字段的完整类型信息。我们来实战一下。

正文

根据字段的 descriptor \text{descriptor} descriptor 的构造规则(细节可以参考 Java 如何理解 class 文件中字段的 descriptor? 一文),可以写出对应的 Parser \text{Parser} Parser,写程序的细节就不赘述了,代码应该还算比较直白。具体的代码如下

代码

请将以下代码保存为 FieldDescriptorParser.java

java 复制代码
import java.util.Map;

public class FieldDescriptorParser {

    public interface FieldType {
        String desc();
    }

    private static final Map<String, String> baseTypes = Map.ofEntries(
            Map.entry("B", "byte"),
            Map.entry("C", "char"),
            Map.entry("D", "double"),
            Map.entry("F", "float"),
            Map.entry("I", "int"),
            Map.entry("J", "long"),
            Map.entry("S", "short"),
            Map.entry("Z", "boolean")
    );

    private static final String ARRAY_TYPE_PREFIX = "[";
    private static final String CLASS_TYPE_PREFIX = "L";
    private static final String CLASS_TYPE_POSTFIX = ";";

    public FieldType parse(String descriptor) {
        if (descriptor.startsWith(ARRAY_TYPE_PREFIX)) {
            return parseArrayType(descriptor);
        }
        if (descriptor.startsWith(CLASS_TYPE_PREFIX)) {
            return parseClassType(descriptor);
        }

        return parseBaseType(descriptor);
    }

    private FieldType parseArrayType(String descriptor) {
        FieldType componentType = parse(descriptor.substring(1));
        return () -> componentType.desc() + "[]";
    }

    private FieldType parseClassType(String descriptor) {
        if (!descriptor.endsWith(CLASS_TYPE_POSTFIX)) {
            throw new IllegalArgumentException("Bad descriptor: " + descriptor);
        }
        // drop 'L' and ';' example: Ljava/lang/Object; ====> java/lang/Object
        String internalName = descriptor.substring(1, descriptor.length() - 1);
        // example: java/lang/Object ====> java.lang.Object
        String originalName = internalName.replace('/', '.');
        return () -> originalName;
    }

    private FieldType parseBaseType(String descriptor) {
        if (baseTypes.containsKey(descriptor)) {
            return () -> baseTypes.get(descriptor);
        }
        throw new IllegalArgumentException("Bad descriptor: " + descriptor);
    }

    private void printUsage() {
        System.out.println("Usage: java FieldDescriptorParser [fieldDescriptor ...]");
    }

    public static void main(String[] args) {
        FieldDescriptorParser parser = new FieldDescriptorParser();
        if (args.length == 0) {
            parser.printUsage();
            System.exit(0);
        }

        for (String arg : args) {
            FieldType fieldType = parser.parse(arg);
            String message = String.format("For field descriptor: [%s], its original type is: [%s]",
                    arg, fieldType.desc());
            System.out.println(message);
        }
    }
}

编译和运行

使用如下的命令可以编译 FieldDescriptorParser.java \text{FieldDescriptorParser.java} FieldDescriptorParser.java

bash 复制代码
javac FieldDescriptorParser.java

编译之后,当前目录会多出一个名为 FieldDescriptorParser.class \text{FieldDescriptorParser.class} FieldDescriptorParser.class 的文件。下方的命令可以运行 FieldDescriptorParser \text{FieldDescriptorParser} FieldDescriptorParser 类中的 main \text{main} main 方法

bash 复制代码
java FieldDescriptorParser

该命令的输出如下

text 复制代码
Usage: java FieldDescriptorParser [fieldDescriptor ...]

说明我们的使用方法不对(至少应该提供一个 descriptor \text{descriptor} descriptor 作为参数)。

为了验证各种情况,我使用了很多 descriptor \text{descriptor} descriptor ⬇️ (这里的 descriptor \text{descriptor} descriptor 都来自 Java 如何理解 class 文件中字段的 descriptor? 一文)

bash 复制代码
java FieldDescriptorParser 'B' 'C' 'D' 'F' 'I' 'J' 'S' 'Z' '[I' \
'[[D' '[[[[J' 'Ljava/lang/Boolean;' 'Ljava/lang/Long;' \
'Ljava/lang/Object;' 'Ljava/util/List;' 'Ljava/util/Map;' \
'Ljava/util/List;' 'Ljava/lang/Void;' '[Ljava/lang/Boolean;' \
'[[[Ljava/lang/Integer;'

这个命令的运行结果如下 ⬇️

text 复制代码
For field descriptor: [B], its original type is: [byte]
For field descriptor: [C], its original type is: [char]
For field descriptor: [D], its original type is: [double]
For field descriptor: [F], its original type is: [float]
For field descriptor: [I], its original type is: [int]
For field descriptor: [J], its original type is: [long]
For field descriptor: [S], its original type is: [short]
For field descriptor: [Z], its original type is: [boolean]
For field descriptor: [[I], its original type is: [int[]]
For field descriptor: [[[D], its original type is: [double[][]]
For field descriptor: [[[[[J], its original type is: [long[][][][]]
For field descriptor: [Ljava/lang/Boolean;], its original type is: [java.lang.Boolean]
For field descriptor: [Ljava/lang/Long;], its original type is: [java.lang.Long]
For field descriptor: [Ljava/lang/Object;], its original type is: [java.lang.Object]
For field descriptor: [Ljava/util/List;], its original type is: [java.util.List]
For field descriptor: [Ljava/util/Map;], its original type is: [java.util.Map]
For field descriptor: [Ljava/util/List;], its original type is: [java.util.List]
For field descriptor: [Ljava/lang/Void;], its original type is: [java.lang.Void]
For field descriptor: [[Ljava/lang/Boolean;], its original type is: [java.lang.Boolean[]]
For field descriptor: [[[[Ljava/lang/Integer;], its original type is: [java.lang.Integer[][][]]

解析结果符合预期

其他

FieldDescriptorParser.java \text{FieldDescriptorParser.java} FieldDescriptorParser.java 的核心逻辑是我自己写的(在 IntelliJ IDEA (Community Edition) 中写的)。我借助 trae 完成了 printUsage \text{printUsage} printUsage 方法的逻辑(这个方法重要性低)。所以整体而言,没有版权问题,读者朋友可以随意使用 FieldDescriptorParser.java \text{FieldDescriptorParser.java} FieldDescriptorParser.java(在遵守法律的前提下),不用告知我。

参考资料

相关推荐
是苏浙17 分钟前
Java实现链表1
java·开发语言
未若君雅裁20 分钟前
上传数据安全:对称加密、非对称加密、签名与重放防护
java·安全
可乐ea28 分钟前
【Spring Boot + MyBatis|第7篇】JWT 登录认证与拦截器实现
java·spring boot·后端·mybatis·状态模式
步步为营DotNet42 分钟前
借助 C# 14 特性强化 .NET 后端数据验证的深度实践
java·c#·.net
西安邮电大学1 小时前
有关栈的经典算法题
java·后端·其他·算法·面试
手握风云-1 小时前
ProtoBuf:从序列化原理到高性能架构底座(一)
java·网络·架构
摇滚侠1 小时前
SpringMVC 入门到实战 配置类替换 XML 配置文件 86-91
xml·java·后端·spring·maven·intellij-idea
栗子~~1 小时前
金融场景下BigDecimal 运算规范 + 常用场景使用 + 数据库字段设计详解
java·数据库·金融
我登哥MVP1 小时前
SpringCloud Alibaba 核心组件解析:服务注册与发现(Nacos)
java·spring boot·后端·spring·spring cloud·java-ee·maven
兰令水1 小时前
leecodecode【单调栈】【2026.6.12打卡-java版本】
java·开发语言·算法