Расхождение между Java и Groovy во внутреннем / внешнем классе

Джава:

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");
   }
}

Выход при запуске:

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()
    }
  }
}

Выход при запуске:

$ 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)

Почему это несоответствие? С помощью Outer.outer() работает, однако, любой способ избежать ввода имени класса?

2 ответа

Решение

Вы можете добавить import static Outer.outer в начало сценария, чтобы не вводить имя класса (вроде)... по крайней мере, вы не должны вводить его внутри метода.

В дополнение к уже предоставленному объяснению, если вы проверите AST Browser внутри Groovy Console на этапе Output, вы увидите, что оба класса являются верхними, поэтому "Inner" не может разрешить методы Outer без импорта.

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 { 

}

Поведение Groovy по умолчанию является динамическим: оно разрешает ссылку во время выполнения, а не во время компиляции. В Java компилятор распознает вызов outer() является статическим и фактически разрешает его в родительский класс. В байт-коде вы найдете полную ссылку на вызываемый статический метод. (В этом случае родительский класс.) Groovy, напротив, разрешает вызов во время выполнения (если вы не используете @CompileStatic аннотации), и поэтому байт-код, сгенерированный компилятором Groovy, не будет иметь полной ссылки. Поэтому во время выполнения Groovy не будет знать, что метод найден только в родительском классе, он просто попытается разрешить его во внутреннем классе, что приведет к сбою.

Незначительное несоответствие: ваши методы Groovy возвращаются Objectв то время как методы Java void, Это не обязательно имеет большое значение, но это создаст проблемы совместимости, если ваш Java-код вызывает Groovy-объекты и вы вносите изменения.

Другие вопросы по тегам