enumの抽象メソッドはgroovyでサポートされてない? #jggug #q

表題の通り。
groovyでenum書いててコンパイルできなかったのでメモ。
(構文ミスってるかと思って何度も確認したので。。。)

前提

javaではenumで抽象メソッドを定義することが可能です。
これにより、新たに値を増やした際に抽象メソッドの実装漏れを防ぐことが可能です。

enum Hoge {
    HUGA {
        @Override
        void method(String name) {
        }
    },
    PIYO {
        @Override
        void method(String name) {
        }
    };
    abstract void method(String name);
}

当然コンパイルも可能。

C:\20130107>dir
<略>
 C:\20130107 のディレクトリ

2013/01/07  20:10    <DIR>          .
2013/01/07  20:10    <DIR>          ..
2013/01/07  20:11               232 Hoge.java
<略>
C:\20130107>javac Hoge.java

C:\20130107>dir
<略>
 C:\20130107 のディレクトリ

2013/01/07  20:11    <DIR>          .
2013/01/07  20:11    <DIR>          ..
2013/01/07  20:11               361 Hoge$1.class
2013/01/07  20:11               381 Hoge$2.class
2013/01/07  20:11               943 Hoge.class
2013/01/07  20:11               232 Hoge.java
<略>

groovycでコンパイル

拡張子のみ変更してGroovyでコンパイルしようとしたらコンパイルエラー

C:\20130107>dir
<略>
 C:\20130107 のディレクトリ

2013/01/07  20:12    <DIR>          .
2013/01/07  20:12    <DIR>          ..
2013/01/07  20:11               232 Hoge.groovy
<略>

C:\20130107>%GROOVY_HOME%/bin/groovyc Hoge.groovy
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
Hoge.groovy: 1: Can't have an abstract method in a non-abstract class. The class
 'Hoge' must be declared abstract or the method 'void method(java.lang.String)'
must be implemented.
 @ line 1, column 1.
   enum Hoge {
   ^

Hoge.groovy: 14: Can't have an abstract method in a non-abstract class. The clas
s 'Hoge' must be declared abstract or the method 'void method(java.lang.String)'
 must not be abstract.
 @ line 14, column 5.
       abstract void method(String name);
       ^

2 errors

抽象メソッドは抽象クラスのみに設定可能だよ!って怒られてるみたい。
javacでコンパイルできたし構文等は間違ってないと思うので、groovyの仕様?

暫定対応

空実装のメソッドを定義してとりあえずの暫定対応。

enum Hoge {
    HUGA {
        void method(String name) {
        }
    },
    PIYO {
        void method(String name) {
        }
    };
    void method(String name) {
        throw new UnsupportedOperationException()
    }
}
C:\20130107>dir
<略>
 C:\20130107 のディレクトリ

2013/01/07  20:12    <DIR>          .
2013/01/07  20:12    <DIR>          ..
2013/01/07  20:12               280 Hoge.groovy
<略>
C:\20130107>%GROOVY_HOME%/bin/groovyc Hoge.groovy

C:\20130107>dir
<略>
 C:\20130107 のディレクトリ

2013/01/07  20:13    <DIR>          .
2013/01/07  20:13    <DIR>          ..
2013/01/07  20:13             4,214 Hoge$1.class
2013/01/07  20:13             4,214 Hoge$2.class
2013/01/07  20:13             6,894 Hoge.class
2013/01/07  20:12               280 Hoge.groovy
<略>

# 本題からはずれるけどjavacとgroovycで10倍もサイズの差が出るものなのね。

課題

これでもまぁ、とりあえず問題はない。
が、最初に書いた通り値を増やした時にメソッド実装し忘れた場合にコンパイラに怒ってもらいたい所。

その他

↓これ見て

これどうやるの?こんなのない?ってのは、Twitterで聞くよな。
#jggug #q みたないなハッシュタグが決まってたほうが拾ってくれる可能性が高まるんだろうな。
ハッシュタグが広まるかどうかが問題だが。

2012年12月 Groovyist/G*のあれやこれや - みちしるべ

↓こんなん言ったら

↓こんなん言われたのでハッシュタグ付けてみた。

追記(2013/01/08)

enumにinterfaceで定義した場合も同様の現象が発生した。

javacでコンパイル

enum Hoge implements IF {
    HUGA {
        @Override
        public void method(String name) {

        }
    },
    PIYO {
        @Override
        public void method(String name) {

        }
    };
}

interface IF {
    void method(String name);
}
C:\20130108>dir
<略>
 C:\20130108 のディレクトリ

2013/01/08  20:04    <DIR>          .
2013/01/08  20:04    <DIR>          ..
2013/01/08  20:04               272 Hoge3.java
<略>
C:\20130108>javac Hoge3.java

C:\20130108>dir
<略>
 C:\20130108 のディレクトリ

2013/01/08  20:05    <DIR>          .
2013/01/08  20:05    <DIR>          ..
2013/01/08  20:05               362 Hoge$1.class
2013/01/08  20:05               382 Hoge$2.class
2013/01/08  20:05               917 Hoge.class
2013/01/08  20:04               272 Hoge3.java
2013/01/08  20:05               129 IF.class
<略>

groovycでコンパイル

C:\20130108>dir
<略>
 C:\20130108 のディレクトリ

2013/01/08  20:06    <DIR>          .
2013/01/08  20:06    <DIR>          ..
2013/01/08  20:04               272 Hoge3.groovy
               1 個のファイル                 272 バイト
               2 個のディレクトリ   3,556,233,216 バイトの空き領域

C:\20130108>%GROOVY_HOME%/bin/groovyc Hoge3.groovy
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
Hoge3.groovy: 1: Can't have an abstract method in a non-abstract class. The clas
s 'Hoge' must be declared abstract or the method 'void method(java.lang.String)'
 must be implemented.
 @ line 1, column 1.
   enum Hoge implements IF {
   ^

1 error

暫定対応

この場合も、とりあえずの空実装をenumクラスに追加すればコンパイルはできました。

enum Hoge implements IF {
    HUGA {
        @Override
        void method(String name) {

        }
    },
    PIYO {
        @Override
        void method(String name) {

        }
    };
    @Override
    void method(String name) {
        throw new UnsupportedOperationException()
    }
}

interface IF {
    void method(String name);
}

※ ↑のコードをjavacでコンパイルする場合にはpublic付けないとエラーになります。

# blogに書いて気づいたけど、enumの場合はclassと異なるから「Hoge3.java」でenumの名前が「Hoge」でもコンパイルできるんですね。

回答

id:nobeansさんが回答をくれました。
「enumの抽象メソッドはgroovyでサポートされてない?」「Yes. でもそれだけじゃねーんだぜ」 #jggug #q - 豆無日記

上記コードはそのまま使えますが、コンストラクタを呼び出すように設定するとコンパイル云々だけではなくて、メソッドがオーバーライドされないです。
つまり、このコードを実行すると「PIYO」が出力されるはずですが。。。

enum Hoge {
    HUGA() {
        @Override
        void method() {
          println "HUGA"
        }
    },
    PIYO() {
        @Override
        void method() {
          println "PIYO"
        }
    }
    void method() {
        throw new UnsupportedOperationException()
    }
}
Hoge.PIYO.method()

「UnsupportedOperationException」が投げられます。

Exception thrown
1 08, 2013 21:06:04 午前 org.codehaus.groovy.runtime.StackTraceUtils sanitize

警告: Sanitizing stacktrace:

java.lang.UnsupportedOperationException
<略>
java.lang.UnsupportedOperationException
<略>
	at ConsoleScript63.run(ConsoleScript63:22)

う〜ん。。。
コンパイルできないならともかく、メソッドがオーバーライドされないのは挙動が異なるわけで。。。
groovyでenum使うのは控えたほうがいいかも?(今は良くても後で抽象メソッド付けるかもしれないし。)