如果类之外有一行代码,为什么Groovy不执行程序中的类?

时间:2018-12-07 02:05:35

标签: groovy

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

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

如果我有一个“ println”语句,为什么Example类不能运行?

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

println "Test"

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

  

Hello World

     

测试

当类外还有另一行时,为什么类不执行?

1 个答案:

答案 0 :(得分:5)

您需要注意一些事项。当您创建具有以下内容的脚本(我们将其称为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.groovygroovyc 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()方法中使用的闭包

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

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()方法,并执行它。