main メソッドが見つからないといわれたら・・・

先日、Scala プログラムを書いていたときのことです。object に main メソッドを定義しているにもかかわらず、main メソッドが見つからないといわれました。
以下のようなプログラムです。

import java.awt.Dimension
import javax.swing.JFrame

object Sample {
  def main(args: Array[String]): Unit = {
    val sample = new Sample
    sample.setVisible(true)
    sample.pack()
  }
}

class Sample extends JFrame {
  setPreferredSize(new Dimension(320, 240))
  setTitle("Sample")
}

上記をコンパイル&実行すると・・・


c:\development\workspaces\scala-sandbox\misc>scalac Sample.scala

c:\development\workspaces\scala-sandbox\misc>scala Sample
java.lang.NoSuchMethodException: Sample.main([Ljava.lang.String;)

というように、main メソッドが見つからないという状態になります。

コンパニオンオブジェクトは、バイトコード上では $ の付加されたクラス名となるようです。このため、Sample.main ではコンパニオンクラスの main メソッドを参照しようとして、見つからないという状態になっています。


では、$ をつければ実行できるのかと思い試しました。


c:\development\workspaces\scala-sandbox\misc>scala Sample$
java.lang.NoSuchMethodException: Sample$.main is not static
今度は not static といわれてしまい、やはり実行できませんでした。

コンパニオンオブジェクトのメソッドは static ではないようです。javap してみると以下のとおり、たしかに static ではなかったです。

Compiled from "Sample.scala"
public final class Sample$ extends java.lang.Object implements scala.ScalaObject{
    public static final Sample$ MODULE$;
    public static {};
    public Sample$();
    public void main(java.lang.String[]);
    public int $tag()       throws java.rmi.RemoteException;
}


一方、object が対になる class を持たない場合は、$ ありも $ なしも、両方ともに object のメソッドが定義されていました。
class Sample を class SampleFrame に変更して、コンパイル&javap してみました。

== javap Sample$ の結果 ==
Compiled from "Sample.scala"
public final class Sample$ extends java.lang.Object implements scala.ScalaObject{
    public static final Sample$ MODULE$;
    public static {};
    public Sample$();
    public void main(java.lang.String[]);
    public int $tag()       throws java.rmi.RemoteException;
}
== javap Sample の結果 ==
Compiled from "Sample.scala"
public final class Sample extends java.lang.Object{
    public static final void main(java.lang.String[]);
    public static final int $tag()       throws java.rmi.RemoteException;
}

Sample のほうには static なメソッドが、Sample$ の方には static ではない同名のメソッドが入っていました。
コップ本にもこのあたりのことが書いてあったような気がするのですが、今手元にないので今度確認します。

教訓: main メソッドを含む object はコンパニオンにしてはいけない。