【问题标题】:Why does Groovy not execute a class in a program if there is a line of code outside the class?为什么Groovy不执行程序中的类,如果在类之外有一行代码?
【发布时间】:2019-05-08 19:02:33
【问题描述】:

Groovy 版本:2.4.5 JVM:1.8.0_151 供应商:Oracle Corporation 操作系统:Linux

我尝试了两个版本的 Groovy 程序。如果程序中没有其他内容(例如,没有'println“Test”'),则“示例”类运行。

如果我有“println”语句,为什么示例类不运行?

class Example {
   static void main(String[] args) {
      def clos = {println "Hello World"};
      clos.call();
   }
}

println "Test"

我希望上面的程序在运行时会打印出来:

你好世界

测试

为什么类外还有一行不执行?

【问题讨论】:

    标签: groovy


    【解决方案1】:

    您需要注意一些事项。当您创建一个包含以下内容的脚本(我们称之为someScript.groovy)时:

    #!groovy
    
    println "Test"
    
    println 21 + 21
    

    它被编译为以下类:

    //
    // Source code recreated from a .class file by IntelliJ IDEA
    // (powered by Fernflower decompiler)
    //
    
    import groovy.lang.Binding;
    import groovy.lang.Script;
    import org.codehaus.groovy.runtime.InvokerHelper;
    
    public class someScript extends Script {
        public someScript() {
        }
    
        public someScript(Binding context) {
            super(context);
        }
    
        public static void main(String... args) {
            InvokerHelper.runScript(someScript.class, args);
        }
    
        public Object run() {
            ((someScript)this).println("Test");
            Object var10000 = null;
            ((someScript)this).println(21 + 21);
            return null;
        }
    }
    

    如您所见,Groovy 脚本的主体在生成的类中表示为方法run()。当我们向这个脚本添加一个类时,假设您的问题中的 Example 类,run() 方法的主体根本不会改变 - 该类被编译为一个 Example.class 字节码文件,就是这样:

    //
    // Source code recreated from a .class file by IntelliJ IDEA
    // (powered by Fernflower decompiler)
    //
    
    import groovy.lang.Closure;
    import groovy.lang.GroovyObject;
    import groovy.lang.MetaClass;
    import org.codehaus.groovy.runtime.DefaultGroovyMethods;
    import org.codehaus.groovy.runtime.GeneratedClosure;
    
    public class Example implements GroovyObject {
        public Example() {
            MetaClass var1 = this.$getStaticMetaClass();
            this.metaClass = var1;
        }
    
        public static void main(String... args) {
            class _main_closure1 extends Closure implements GeneratedClosure {
                public _main_closure1(Object _outerInstance, Object _thisObject) {
                    super(_outerInstance, _thisObject);
                }
    
                public Object doCall(Object it) {
                    DefaultGroovyMethods.println(Example.class, "Hello World");
                    return null;
                }
    
                public Object call(Object args) {
                    return this.doCall(args);
                }
    
                public Object call() {
                    return this.doCall((Object)null);
                }
    
                public Object doCall() {
                    return this.doCall((Object)null);
                }
            }
    
            Closure clos = new _main_closure1(Example.class, Example.class);
            clos.call();
        }
    }
    

    当我们运行 Groovy 编译器来编译 someScript.groovy (groovyc someScript.groovy) 并列出生成的类时,我们会看到如下内容:

    ls -lh *.class
    -rw-rw-r--. 1 wololock wololock 2,0K 12-07 10:26  Example.class
    -rw-rw-r--. 1 wololock wololock 1,6K 12-07 10:26 'Example$_main_closure1.class'
    -rw-rw-r--. 1 wololock wololock 1,4K 12-07 10:26  someScript.class
    

    注意:这个Example$_main_closure1.class 表示Example.main() 方法中使用的闭包

    现在,让我们看看如果我们从 someScript.groovy 文件中注释(或删除)println 语句并编译它会发生什么:

    someScript.groovy

    #!groovy
    
    class Example {
        static void main(String[] args) {
            def clos = {println "Hello World"};
            clos.call();
        }
    }
    
    //println "Test"
    //
    //println 21 + 21
    

    编译时间:

    > groovyc someScript.groovy
    
    > ls -lh *.class
    -rw-rw-r--. 1 wololock wololock 2,0K 12-07 10:31  Example.class
    -rw-rw-r--. 1 wololock wololock 1,6K 12-07 10:31 'Example$_main_closure1.class'
    

    如您所见,没有生成someScript.class 类文件。发生这种情况是因为我们刚刚编译的脚本文件不包含任何正文,但它里面有Example 类。当您运行这样的脚本时,Groovy 会尝试定位第一个静态 main() 方法来执行它 - 这就是运行以下脚本会产生 Hello World 输出的原因:

    > groovy someScript.groovy 
    Hello World
    

    让我们更进一步,在 someScript.groovy 文件的顶部添加另一个类:

    someScript.groovy

    #!groovy 
    
    class Foo {
        static void main(String[] args) {
            println "Bar"
        }
    }
    
    
    class Example {
        static void main(String[] args) {
            def clos = {println "Hello World"};
            clos.call();
        }
    }
    
    //println "Test"
    //
    //println 21 + 21
    

    脚本的主体仍然被注释掉。让我们编译看看生成了哪些类文件:

    > groovyc someScript.groovy
    
    > ls -lh *.class
    -rw-rw-r--. 1 wololock wololock 2,0K 12-07 10:35  Example.class
    -rw-rw-r--. 1 wololock wololock 1,6K 12-07 10:35 'Example$_main_closure1.class'
    -rw-rw-r--. 1 wololock wololock 1,8K 12-07 10:35  Foo.class
    

    正如预期的那样,我们可以看到 3 个类文件。让我们看看如果我们使用groovy 命令运行脚本会发生什么:

    > groovy someScript.groovy                             
    Bar
    

    现在你可以看到Foo.main() 方法被执行了,因为Groovy 将该方法定位在脚本文件的顶部,并且假设这是我们要运行的主要方法。

    让我们用包含两个类和脚本主体的示例来完成这个:

    someScript.groovy

    #!groovy
    
    class Foo {
        static void main(String[] args) {
            println "Bar"
        }
    }
    
    
    class Example {
        static void main(String[] args) {
            def clos = {println "Hello World"};
            clos.call();
        }
    }
    
    println "Test"
    
    println 21 + 21
    

    编译时间:

    > groovyc someScript.groovy
    
    > ls -lh *.class
    -rw-rw-r--. 1 wololock wololock 2,0K 12-07 10:39  Example.class
    -rw-rw-r--. 1 wololock wololock 1,6K 12-07 10:39 'Example$_main_closure1.class'
    -rw-rw-r--. 1 wololock wololock 1,8K 12-07 10:39  Foo.class
    -rw-rw-r--. 1 wololock wololock 1,4K 12-07 10:39  someScript.class
    

    这次生成了一个类someScript,因为脚本主体不为空。最后看一下生成的someScript.class文件:

    //
    // Source code recreated from a .class file by IntelliJ IDEA
    // (powered by Fernflower decompiler)
    //
    
    import groovy.lang.Binding;
    import groovy.lang.Script;
    import org.codehaus.groovy.runtime.InvokerHelper;
    
    public class someScript extends Script {
        public someScript() {
        }
    
        public someScript(Binding context) {
            super(context);
        }
    
        public static void main(String... args) {
            InvokerHelper.runScript(someScript.class, args);
        }
    
        public Object run() {
            ((someScript)this).println("Test");
            Object var10000 = null;
            ((someScript)this).println(21 + 21);
            return null;
        }
    }
    

    如您所见,与我们的第一个示例相比(当脚本中没有类,但只有两个 println 语句时),它并没有改变,所以除了运行 someScript.run() 方法之外,我们不能指望其他任何事情发生.让我们运行脚本:

    > groovy someScript.groovy
    Test
    42
    

    结论

    • 当您创建 Groovy 脚本时,它的主体会被移动并编译为 scriptName.run() 方法,然后会被执行。
    • 如果你将一个带有main() 方法的类添加到Groovy 脚本并保留脚本主体,添加的类main() 方法将不会被执行——它只会编译类,你可以在你的脚本正文(如果需要)。
    • 如果你将一个带有main() 方法的类添加到一个Groovy 脚本并且你没有放置任何脚本主体(任何在类之外的语句/表达式),那么Groovy 会搜索第一个静态main() 方法,并且它执行它。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-03-05
      • 1970-01-01
      • 1970-01-01
      • 2022-01-05
      • 1970-01-01
      • 1970-01-01
      • 2011-12-18
      • 1970-01-01
      相关资源
      最近更新 更多