1954

Thoughts, stories and ideas.

JavaとしてもScalaとしてもコンパイルできるHelloWorld

タイトルの通り、以下のコードはJavaとしてもScalaとしてもコンパイルできます。

/*/**/
class JavaMain {
    public static void main(String args[]) {
        System.out.println("Hello, World");
    }
}
// */

/*/**/
class App {}
// */

/*/**/
class A {
    int object = 1;
    int ScalaMain = 1;

    void foo() {
        int i =
        // */
        object
        /*/**/
        ;

        i = // */
        ScalaMain
        /*/**/
        ;
    }

    class B // */
    extends App {
        /*/**/
        void bar() { // */
            System.out.println("Hello, World");
        /*/**/ } // */
    }
/*/**/
}
// */

↑をPolyglot.java, Polyglot.scalaとして保存して試してみましょう。

$ javac Polyglot.java
$ java JavaMain
Hello, World
$ rm -f *.class
$ scalac Polyglot.scala
$ scala ScalaMain
Hello, World

わぁい動いた!

Java, Scalaのバージョンは以下です。

$ javac -version
javac 1.8.0_77
$ scalac -version
Scala compiler version 2.12.3 -- Copyright 2002-2017, LAMP/EPFL and Lightbend, Inc.

解説

ScalaJavaにおける、ブロックコメントの挙動の違いがポイントです。

Scalaでは、ブロックコメントをネストさせた場合に/**/の対応をきちんと取りますが、Javaでは、何個/*を開こうが、*/が来た時点でブロックコメントが終了します。

Java:

/* -- (1)
  /*
   */ -- (1)が閉じる
 */ -- 余ってるのでsyntax error

Scala:

/* -- (1)
  /* -- (2)
   */ -- (2)
 */ -- (1)

これを利用すると、javacにだけ認識されてscalacには無視されるコードを書くことができます。

いっぽう、scalacにだけ認識されるコードを書くのは難しいので、javacでコンパイルできるようにうまいこと色々足しています。

  • L2..L6: javacだけに認識されるMainクラス
  • L10: scala.Appを使ってScalaのMainクラスを作るので、javacで通るように空のAppを用意しておく
  • L14..L39: javacで通るように色々挟みながら、object ScalaMain extends App { System.out.println("Hello, World"); }と書いている

まとめ

今回のコードはもちろんGroovyでもコンパイルできるので、Java, Scala, GroovyのPolyglotができたことになります。

同じ感じでKotlinもいけるかな?