Java vs. Groovy inner / outer class discrepancy
Asked Answered
B

2

7

Java:

public final class Outer {
   public static void main(String[] args) {
      Inner.inner();
   }

   private static final class Inner {
      private static void inner() {
         System.out.println("inner");
         outer();
      }
   }

   private static void outer() {
      System.out.println("outer");
   }
}

Output when run:

inner
outer

Groovy:

public final class Outer {
  static main(String[] args) {
    Inner.inner()
  }

  static outer() {
    println('outer')
  }

  static final class Inner {
    static inner() {
      println('inner')
      outer()
    }
  }
}

Output when run:

$ groovy Outer
inner
Caught: groovy.lang.MissingMethodException: No signature of method: static Outer$Inner.outer() is applicable for argument types: () values: []
Possible solutions: inner(), use([Ljava.lang.Object;), use(java.lang.Class, groovy.lang.Closure), use(java.util.List, groovy.lang.Closure), putAt(java.lang.String, java.lang.Object), grep()
groovy.lang.MissingMethodException: No signature of method: static Outer$Inner.outer() is applicable for argument types: () values: []
Possible solutions: inner(), use([Ljava.lang.Object;), use(java.lang.Class, groovy.lang.Closure), use(java.util.List, groovy.lang.Closure), putAt(java.lang.String, java.lang.Object), grep()
        at Outer$Inner.inner(Outer.groovy:13)
        at Outer$Inner$inner.call(Unknown Source)
        at Outer.main(Outer.groovy:3)

Why this discrepancy? Using Outer.outer() works, however any way to avoid typing the class name?

Bespangle answered 30/10, 2014 at 17:28 Comment(0)
B
5

You can add an import static Outer.outer to top of the script to avoid typing the class name (kinda)...you at least avoid typing it inside the method.

To complement the already-provided explanation, if you inspect the AST Browser inside Groovy Console at the Output stage, you can see that both classes are top-level, so "Inner" can't resolve to Outer's methods without the import.

final public class Outer implements groovy.lang.GroovyObject extends java.lang.    Object { 

}
final public static class Outer$Inner implements groovy.lang.GroovyObject extends java.lang.Object { 

}
Boob answered 30/10, 2014 at 20:45 Comment(0)
H
3

Groovy's default behavior is dynamic: it resolves the reference at runtime, not compile time. In Java, the compiler recognizes the call to outer() is static and actually resolves it to the parent class. In the bytecode, you will find a fully-qualified reference to the static method being invoked. (The parent class in this case.) Groovy, by contrast, resolves the call at runtime (unless you use the @CompileStatic annotation) and so the bytecode generated by the Groovy compiler will not have a fully-qualified reference. Therefore, at runtime, Groovy will not know that the method is found only in the parent class, it will simply try to resolve it in the inner class, which will fail.

Minor discrepancy: your Groovy methods are returning Object, while the Java methods are void. This isn't necessarily a big deal, but it will create compatibility problems if your Java code is calling Groovy objects and you make changes.

Halvaard answered 30/10, 2014 at 20:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.