Apache Groovyのバージョン4.0.0には、switch式、sealed型、組込みの型チェック、組込みマクロメソッドの他、インキュベーション機能としてレコード、JavaShell、POJOアノテーション、Groovyコントラクト、Groovy-Integrated Query、TOMLのサポートが導入されている。それら以外にも、いくつかの小さな改善や、今回の最新バージョンで削除された機能による非互換的な変更が含まれる。
また、MavenのgroupId
がorg.codehaus.groovy
からorg.apache.groovy
に変更されているので、Groovy 4.0.0にアップグレードする場合には、依存関係を修正することが推奨されている。
Java Platform Module System(JPMS)は、複数のモジュールに同名のクラスやパッケージを置くことを許可していない。Groovy 3ではまだクラスの重複バージョンが提供されていたが、Groovy 4ではこれらが削除されているため、groovy.xml.XmlSlurper
、groovy.xml.XmlParser
、groovy.ant.AntBuilder
、groovy.test.GroovyTestCase
などの新たなクラスを使用する必要がある。
既存のswitch文に加えて、switch式がサポートされるようになった。
def result = switch(i) {
case 0 -> 'January'
case 1 -> 'February'
//...
default -> throw new IllegalStateException('Invalid number of month')
}
コードブロックを使用して、複数の文を記述することもできる。
case 0 -> { def month = 'January'; def year = Year.now().getValue();
month + " " + year }
switchの実装はJavaとは違い、可能な値すべてのcaseブランチを記述する必要はない。defaultブランチが提供されていない場合は、Groovyがnull
を返すdefaultブランチを暗黙的に追加する。
sealed
キーワードまたは@Sealed
アノテーションを使って、sealedクラスを作ることが可能になった。許可されるサブクラスは、同時コンパイルされる場合には自動的に検出される。sealed
キーワードにpermits
クラウズを使用するか、@Sealed
アノテーションにpermitted subclass
属性を使用することで、許可されるサブクラスを明示的に定義することもできる。
@Sealed(permittedSubclasses = [Dog, Cat]) interface Animal {}
@Singleton final class Dog implements Animal {
String toString() { 'Dog' }
}
@Singleton final class Cat implements Animal {
String toString() { 'Cat' }
}
sealed interface Animal permits Dog, Cat {}
@Singleton final class Dog implements Animal {
String toString() { 'Dog' }
}
@Singleton final class Cat implements Animal {
String toString() { 'Cat' }
}
新たなインキュベーション機能のひとつであるレコードは、Groovyの既存機能である@Immutable
に非常に近いものだ。
record Student(String firstName, String lastName) { }
定義されたクラスは暗黙的にfinal
となり、private
final
フィールドの
firstName
とlastName
、firstName()
とlastName()
メソッド、2つを引数とするデフォルトコンストラクタ、0L
のserialVersionUID
、toString
、equals()
、hashCode()
の各メソッドが自動的に生成される。
弱い型チェックと強い型チェックの両方が、組み込み型チェックとして提供される。今回のリリースでは、いくつかの型チェッカを含んだgroovy-typechekersモジュールが導入された。このモジュールをプロジェクトに追加した上で@TypeChecked
アノテーションを使えば、コンパイル中に記述の有効性を検証することが可能になる。以下の文は閉じカッコが不足しており、通常ならば実行時にエラーとなるものだ。
@TypeChecked(extensions = 'groovy.typecheckers.RegexChecker')
def year() {
def year = '2022'
def matcher = year =~ /(\d{4}/
}
しかし型チェッカが、コンパイル時に次のようなエラーを指摘してくれる。
Groovyc: [Static type checking] — Bad regex: Unclosed group near index 6
(\d{4}
型チェックと同じように、いくつかのマクロメソッドがgroovy-macro-libraryモジュールを通じて利用可能になった。例えばSV
マクロでは、変数名と値からStringを生成することができる。
def studentName = "James"
def age = 42
def courses = ["Introduction to Java" , "Java Concurrency", "Data structures"]
println SV(studentName, age, courses)
studentName=James, age=42, courses=[Introduction to Java, Java Concurrency,
Data structures]
NV
マクロはNamedValueを生成して、名前と値の処理を可能にする。
def namedValue = NV(age)
assert namedValue instanceof NamedValue
assert namedValue.name == 'age' && namedValue.val == 42
同じインキュベーション機能のJavaShellでは、Javaコードスニペットを実行することができる。
import org.apache.groovy.util.JavaShell
def student = 'record Student(String firstName, String lastName) {}'
Class studentClass = new JavaShell().compile('Student', student)
assert studentClass.newInstance("James", "Gosling")
.toString() == 'Student[firstName=James, lastName=Gosling]'
POJOアノテーションも同じくインキュベーション機能で、GroovyをLombokのようなプリプロセッサとして使用できるようになる。@POJO
アノテーションは、そのクラスが高度なGroovyオブジェクトではなくPOJOであることと、@CompileStatic
アノテーションの処理が必要であることを示すものだ。
インキュベーションモジュールのgroovy-contractsは、クラス不変(class-invariants)、およびクラスとインターフェースの事前および事後条件の指定を可能にする。
import groovy.contracts.*
@Invariant({ coursesCompleted >= 0 })
class Student {
int coursesCompleted = 0
boolean started = false
@Requires({ started })
@Ensures({ old.coursesCompleted < coursesCompleted })
def processCourses(newCoursesCompleted) {
coursesCompleted += newCoursesCompleted
}
}
started
がtrue
でなければならない、などの事前条件(precondition)が満たされない場合には、エラーメッセージが表示される。
def student = new Student()
student.processCourses(2)
org.apache.groovy.contracts.PreconditionViolation: <groovy.contracts.Requires>
Student.java.lang.Object processCourses(java.lang.Object)
started
|
false
処理されたコースの数が減少した場合など、事後条件(post-condition)が満たされない場合にも、別のエラーメッセージが表示される。
def student = new Student()
student.setStarted(true)
student.processCourses(-2)
org.apache.groovy.contracts.PostconditionViolation: <groovy.contracts.Ensures>
Student.java.lang.Object processCourses(java.lang.Object)
old.coursesCompleted < coursesCompleted
| | | |
| 0 | -2
| false
インキュベーション機能のGroovy-Integrated Query (GINQまたはGQuery)は、リストやマップ、カスタムドメインオブジェクト、その他の構造的データのコレクションをクエリ可能にするもので、SQLに非常に近い。
from student in students
orderby student.age
where student.age > 20
select student.firstName, student.lastName, student.age
from student in students
leftjoin university in universities on student.university == university.name
select student.firstName, student.lastName, university.city
同じくインキュベーションではあるが、TOMLベースファイルのサポートは、groovy-tomlモジュールを使うことで、オブジェクトグラフの構築および解析を可能にする。
def tomlBuilder = new TomlBuilder()
tomlBuilder.records {
student {
firstName 'James'
// ...
}
}
def tomlSlurper = new TomlSlurper()
def toml = tomlSlurper.parseText(tomlBuilder.toString())
assert 'James' == toml.records.student.firstName
その他にも、GString
toString
値のキャッシングによるパフォーマンス向上など、数多くの小さな改善が実施されている。レンジでは、これまではインクルーシブ(1..10
)と右辺エクスクルーシブ(1..<10
)が指定可能だったが、左辺(1<..10
)および両辺(1<..<10
)のエクスクルーシブも指定できるようになった。小数値の先行0が省略可能になり、.5
と0.5
の両方がサポートされた。
Groovy 4には非互換的な変更もいくつかある。Antlr2パーザの削除や、コールサイトベースのバイトコード生成が不可能になったことなどがその例だ。groovy-jaxbやgroovy-bsfなど、いくつかのモジュールも削除されている。groovy-yamlモジュールがgroovy-allpomに含まれるようになった一方で、groovy-testngモジュールが削除された。
非互換的な変更と新機能の完全なリストがリリースノートに掲載されている。