已知抽象父类AST,如何确定对象具体子类并避免强制转换?
我正在使用一个外部库,它可以根据AST的XML文件表示,为一门玩具编程语言构建抽象语法树(AST)的内部表示。我用的fromXML方法返回AST类型,但根据库文档,AST是抽象类,拥有AST.program、AST.expr、AST.stmt_list等多个具体子类。
我的目标是依据AST生成类汇编代码,但因为AST没有字段,必须操作具体类实例。目前我用instanceof运算符判断fromXML返回的AST是否为AST.program,再强制转换为AST.program。这方法现在可行,因为fromXML只会返回AST.program实例,但我想知道有没有更优方案,能不能不强制转换就访问AST.program的字段?
我的代码如下:
import AST; // The AST library import java.io.File; public class CalcBack { public static void main(String[] args) { File f_xml = new File(args[0]); AST.program prog = null; try { AST ast_from_xml = AST.fromXML(f_xml); // May throw Exception if (ast_from_xml instanceof AST.program) { prog = (AST.program) ast_from_xml; // Casting AST to AST.program } else { System.err.println("Not a program!"); } } catch (Exception e) { System.err.println(e); } if (prog == null) { System.exit(1); } System.out.println(prog.statements); // I can do something with prog } }
运行输出:
% java -cp AST.jar CalcBack.java gcd.xml AST$stmt_list@5c909414
使用Java 16+模式匹配(最简洁)
如果你用的是Java 16或更高版本,模式匹配instanceof可以直接在判断时完成类型转换,省去显式强制转换步骤:try { AST ast_from_xml = AST.fromXML(f_xml); if (ast_from_xml instanceof AST.program prog) { // 直接使用prog访问子类字段 System.out.println(prog.statements); } else { System.err.println("Not a program!"); System.exit(1); } } catch (Exception e) { System.err.println(e); System.exit(1); }这种写法既保留了类型校验,又让代码更简洁安全。
封装类型转换逻辑(兼容低版本Java)
若无法升级Java版本,可把类型检查和转换封装成工具方法,简化主逻辑:private static AST.program castToProgram(AST ast) { if (ast instanceof AST.program) { return (AST.program) ast; } throw new IllegalArgumentException("Not a program AST node"); }主方法调用示例:
try { AST ast_from_xml = AST.fromXML(f_xml); AST.program prog = castToProgram(ast_from_xml); System.out.println(prog.statements); } catch (Exception e) { System.err.println(e); System.exit(1); }检查库是否提供精准API
再核对下外部库的文档,看看有没有专门返回AST.program的方法(比如fromXMLProgram)。如果有,直接调用这类方法就能彻底避免类型转换;如果库只有返回AST的fromXML,那前两种方法就是最优选择。
补充说明
因为AST是抽象类,本身没有具体字段,AST.program的字段是子类特有成员。Java静态类型检查要求明确实例的具体类型才能访问子类成员,所以本质上必须做类型确认,只是可以用更优雅的语法替代生硬的强制转换。
内容的提问来源于stack exchange,提问作者Timo

