Use trait with generics in Groovy
Groovyでジェネリクス付きのtraitを使う場合にちょっとハマったのでメモを残します。
前提
- Groovy 2.3.6
- JDK 7u72
例
下記のコードを実行してみます。
@groovy.transform.Immutable class Person { final String name final int age } trait NamedMap<T> { boolean add(T item) { put(item.name, item) ? false : true } } def peoples = [:] as NamedMap<Person> assert peoples.add(new Person('Taro', 30)) assert peoples.Taro.age == 30
このコードを実行すると、以下の実行時エラーが発生します。
org.codehaus.groovy.runtime.typehandling.GroovyCastException
: Error casting map to NamedMap, Reason: startup failed:NamedMap$TraitAdapterwrapper
: -1: A transform used a generics containing ClassNodeNamedMap <T extends java.lang.Object -> java.lang.Object>
for the super classNamedMap$TraitAdapter
directly. You are not supposed to do this. Please create a new ClassNode referring to the old ClassNode and use the new ClassNode instead of the old one. Otherwise the compiler will create wrong descriptors and a potential NullPointerException in TypeResolver in the OpenJDK. If this is not your own doing, please report this bug to the writer of the transform. @ line -1, column -1. 1 error
対処
下記のようにジェネリクス付きtraitを継承したtraitを定義したら解決しました。これが本格対処になるのかは分かりません。
@groovy.transform.Immutable class Person { final String name final int age } trait NamedMap<T> { boolean add(T item) { put(item.name, item) ? false : true } } trait PersonMap implements NamedMap<Person> {} def peoples = [:] as PersonMap assert peoples.add(new Person('Taro', 30)) assert peoples.Taro.age == 30