zoukankan      html  css  js  c++  java
  • 向java的main()传入大量参数

    项目中有一些用java写成的可执行的工具,需要调用者传入大量的参数。最开始,我使用的是最传统的方式,直接一个传入参数数组,于是有如下这么壮观的代码:

    public static void main(String[] args) {
        String aaa = args[0];
        String bbb = args[1];
        String ccc = args[2];
        String ddd = args[3];
        String eee = args[4];
        String fff = args[5];
        String ggg = args[6];
        String hhh = args[7];
        // do something with these arguments
    }
    
    

    调用者:

    java MyTool hello world1 world2 world3 world4 world5 world6 world7
    
    

    客户同事拿去用后,感觉十分不便,向我抱怨。原来他在写脚本调用的时候,必须反复查看我的java源代码,才知道每个参数是做什么的。写完之后,下次需要修改的时候,又忘了还得来查。而且参数顺序还不能错,个数也不能多不能少,总之就是很麻烦。

    我也认识到这个问题确实存在,便问客户同事有没有什么好办法,他给了我这样的例子:

    public static void main(String[] args) {
        String aaa = System.getProperty("aaa");
        String bbb = System.getProperty("bbb");
        String ccc = System.getProperty("ccc");
        String ddd = System.getProperty("ddd");
        String eee = System.getProperty("eee");
        String fff = System.getProperty("fff");
        String ggg = System.getProperty("ggg");
        String hhh = System.getProperty("hhh");
        // do something with these arguments
    }
    
    

    调用者:

    java -Daaa=hello -Dbbb=world1 -Dccc=world2 -Dddd=world3 -Deee=world4 -Dfff=world5 -Dggg=world6 -Dhhh=world7 MyTool
    
    

    客户同事的意思是,这种做法可以“完美”的解决他提到的那几个不便之处,比如:

    • -D后面的参数名可用于提醒参数的意思
    • 顺序怎么写都行
    • 如果哪个参数是可选的,可以直接删除即可

    虽然我第一感觉是“竟然敢用这种方式来传参数!”,但一时之间竟想不出什么话来反对,因为他这种方式能做到的,我那种方式的确做不到。

    细想一下,这两种方式倒是相当互补,我有你无,我无你有。虽然相比起来,我觉得后者问题更多,因为它会污染System属性环境,更容易产生一些不可预料的问题,但也不失为一种有思考价值的方案。

    然后,我想到了这样一种试图结合两者优点的方案:

    public static void main(String[] args) {
        Map map = new HashMap();
        for(int i=0; i
            map.put(args[i], args[i+1]);
        }
        String aaa = map.get("-aaa");
        String bbb = map.get("-bbb");
        String ccc = map.get("-ccc");
        String ddd = map.get("-ddd");
        String eee = map.get("-eee");
        String fff = map.get("-fff");
        String ggg = map.get("-ggg");
        String hhh = map.get("-hhh");
        // do something with these arguments
    }
    
    

    调用者:

    java MyTool -aaa hello -bbb world1 -ccc world2 -ddd world3 -eee world4 -fff world5 -ggg world6 -hhh world7
    
    

    看起来似乎清楚了一些,虽然更长了一点。现在,它也可以:

    • 参数列表中有用作提示参数
    • 顺序怎么写都行
    • 如果有可选参数,直接删除对应项

    而且不污染System属性环境,相比前面的-D方案,要稍好一些。

    只能做到这个程度了吗?看上面的三种方案,实际上都存在着一些问题,需要写更多代码:

    1. 打印usage
    2. 对参数进行检查,如果有缺少的或不合理的,要提示错误

    这些缺失的工作实际上相当烦琐,让我们的代码更加混乱,却又不可或缺。

    有没有优雅一些的方式呢?答案是有,我们可以利用java的annotation去描述我们的参数,使用这些信息对参数进行验证、取值以及打印使用帮助。

    市面上有不少这样的库,比如args4j, jcommands等,我最后选用了args4j。

    首先我们需要写一个类,里面用annotation来描述我们的程序需要什么样的参数:

    public class Args {
        @Option(required=true, name="-aaa",usage="aaa is something")
        private String aaa;
        @Option(name="-bbb",usage="bbb is something")
        private String bbb;
        @Option(name="-ccc",usage="ccc is something")
        private String ccc;
        @Option(name="-ddd",usage="ddd is something")
        private String ddd;
        @Option(name="-eee",usage="eee is something")
        private String eee;
        @Option(name="-fff",usage="fff is something")
        private String fff;
        @Option(name="-ggg",usage="ggg is something")
        private String ggg;
     
        // getters for them
    }
    
    

    这样可以很清楚的方式知道每个参数的作用、描述等。

    然后取值:

    public static void main(String[] args) {
        Args myArgs = new Args();
        CmdLineParser parser = new CmdLineParser(myArgs);
        parser.parseArgument(myArgs);
    }
    
    

    这时,myArgs实例中的各fields,就已经自动赋予了相应的值,是由CmdLineParser通过反射赋值的。

    想打印使用提示呢?

    parser.printUsage(System.out);
    
    

    它会根据Args里的定义,自动组织成一种可读性较好的格式,输出使用提示,十分贴心。

    调用者:

    java MyTool -aaa hello -bbb world1 -ccc world2 -ddd world3 -eee world4 -fff world5 -ggg world6 -hhh world7
    
    

    虽然看起来调用方式跟之前没什么变化,但内涵却更加丰富了,而我们要做的,仅仅是定义一个描述类,和调用三两行代码而已。

    这是我目前认为最优雅的解决方案。

  • 相关阅读:
    conan本地安装包
    Python PIL 怎么知道写入图片格式的kb大小
    怎么对C++枚举(不是类)里面的东西进行随机
    当双方Visual studio windows SDK不一样的时候的解决办法
    不小心使用vcpkg之后再使用conan,一直报链接错误
    关于obj文件的理解
    卸载VS2015之后,安装VS2017出错
    性状、生成器、闭包、OPcache【Modern PHP】
    docker容器修改hosts文件,重启失效问题解决
    微信公众号-模板消息通用接口封装
  • 原文地址:https://www.cnblogs.com/zhangchuan210/p/4729885.html
Copyright © 2011-2022 走看看