Groovy言語の紹介

Groovy言語の紹介

1. 概要

Groovy is a dynamic, scripting language for the JVM。 バイトコードにコンパイルし、Javaコードおよびライブラリとシームレスにブレンドします。

この記事では、基本的な構文、制御構造、コレクションなど、Groovyの重要な機能のいくつかを見ていきます。

次に、null安全性、暗黙の真実、演算子、文字列など、魅力的な言語にする主な機能のいくつかを見ていきます。

2. 環境

MavenプロジェクトでGroovyを使用する場合は、pom.xml:に以下を追加する必要があります。


    
        // ...
        
            org.codehaus.gmavenplus
            gmavenplus-plugin
            1.5
       
   


    // ...
    
        org.codehaus.groovy
        groovy-all
        2.4.10
    

最新のMavenプラグインはhereと最新バージョンのgroovy-allhereで見つけることができます。

3. 基本的な機能

Groovyには多くの便利な機能があります。 それでは、言語の基本的な構成要素と、Javaとの違いを見てみましょう。

それでは、言語の基本的な構成要素と、Javaとの違いを見てみましょう。

3.1. 動的な入力

Groovyの最も重要な機能の1つは、動的型付けのサポートです。

型定義はオプションであり、実際の型は実行時に決定されます。 これらの2つのクラスを見てみましょう。

class Duck {
    String getName() {
        'Duck'
    }
}
class Cat {
    String getName() {
        'Cat'
    }
}

これらの2つのクラスは、同じgetNameメソッドを定義しますが、コントラクトで明示的に定義されていません。

ここで、getNameメソッドを持つアヒルと猫を含むオブジェクトのリストがあると想像してください。 Groovyを使用すると、次のことができます。

Duck duck = new Duck()
Cat cat = new Cat()

def list = [duck, cat]
list.each { obj ->
    println obj.getName()
}

コードがコンパイルされ、上記のコードの出力は次のようになります。

Duck
Cat

3.2. 暗黙の真実の変換

JavaScriptのように、Groovyは必要に応じてすべてのオブジェクトをブール値に評価します。 ifステートメント内で使用する場合、または値を否定する場合:

if("hello") {...}
if(15) {...}
if(someObject) {...}

この変換について覚えておくべきいくつかの簡単なルールがあります。

  • 空でないCollections,配列、マップはtrueに評価されます

  • 少なくとも1つの一致があるMatcherは、trueと評価されます

  • IteratorsおよびEnumerationsとその他の要素はtrueに強制変換されます

  • 空でないStringsGStrings、およびCharSequencesは、trueに強制変換されます

  • ゼロ以外の数値はtrueに評価されます

  • null以外のオブジェクト参照は、trueに強制変換されます

暗黙のトゥルーシネス変換をカスタマイズする場合は、asBoolean()メソッドを定義できます。

3.3. 輸入

一部のパッケージはデフォルトでインポートされますが、明示的にインポートする必要はありません。

import java.lang.*
import java.util.*
import java.io.*
import java.net.*

import groovy.lang.*
import groovy.util.*

import java.math.BigInteger
import java.math.BigDecimal

4. AST変換

AST(Abstract Syntax Tree)変換を使用すると、Groovyコンパイルプロセスにフックして、ニーズに合わせてカスタマイズできます。 これはコンパイル時に行われるため、アプリケーションの実行時にパフォーマンスが低下することはありません。 AST変換を作成できますが、組み込みの変換も使用できます。

変換を作成することも、組み込みの変換を利用することもできます。

知っておく価値のあるいくつかの注釈を見てみましょう。

4.1. 注釈TypeChecked

この注釈は、注釈付きのコード部分に対して厳密な型チェックをコンパイラに強制するために使用されます。 型チェックのメカニズムは拡張可能であるため、必要に応じてJavaで使用可能なものよりもさらに厳密な型チェックを提供することもできます。

以下の例を見てみましょう。

class Universe {
    @TypeChecked
    int answer() { "forty two" }
}

このコードをコンパイルしようとすると、次のエラーが発生します。

[Static type checking] - Cannot return value of type java.lang.String on method returning type int

@TypeCheckedアノテーションは、クラスとメソッドに適用できます。

4.2. 注釈CompileStatic

この注釈により、コンパイラはJavaコードで行われているようにコンパイル時チェックを実行できます。 その後、コンパイラーは静的コンパイルを実行し、Groovyメタオブジェクトプロトコルをバイパスします。

クラスに注釈が付けられている場合、すべてのメソッド、プロパティ、ファイル、内部クラスなど 注釈付きクラスの型チェックが行われます。 メソッドに注釈が付けられると、静的コンパイルは、そのメソッドで囲まれたアイテム(クロージャーと匿名の内部クラス)にのみ適用されます。

5. プロパティ

Groovyでは、JavaのPOJOと同じように機能するPOGO(Plain Old Groovy Objects)を作成できますが、コンパイル中はgetters and setters are automatically generated for public propertiesであるため、よりコンパクトになります。 まだ定義されていない場合にのみ生成されることを覚えておくことが重要です。

これにより、値を設定または取得するときに動作をオーバーライドする機能を保持しながら、属性をオープンフィールドとして定義する柔軟性が得られます。

このオブジェクトを考慮してください。

class Person {
    String name
    String lastName
}

クラス、フィールド、およびメソッドのデフォルトのスコープはpublic –であるため、これはパブリッククラスであり、2つのフィールドはパブリックです。

コンパイラはこれらをプライベートフィールドに変換し、getName()setName()getLastName()、およびsetLasfName()メソッドを追加します。 特定のフィールドにsettergetterを定義すると、コンパイラーはパブリックメソッドを作成しません。

5.1. ショートカット表記

Groovyは、プロパティを取得および設定するためのショートカット表記を提供します。 Javaのゲッターとセッターを呼び出す代わりに、フィールドのようなアクセス表記を使用できます。

resourceGroup.getResourcePrototype().getName() == SERVER_TYPE_NAME
resourceGroup.resourcePrototype.name == SERVER_TYPE_NAME

resourcePrototype.setName("something")
resourcePrototype.name = "something"

6. オペレータ

ここで、プレーンJavaで知られている演算子に加えて追加された新しい演算子を見てみましょう。

6.1. ヌルセーフ間接参照

最も一般的なものは、nullセーフな間接参照演算子“?”です。これにより、メソッドを呼び出すとき、またはnullオブジェクトのプロパティにアクセスするときに、NullPointerExceptionを回避できます。 これは、チェーンのある時点でnull値が発生する可能性があるチェーン呼び出しで特に役立ちます。

たとえば、安全に呼び出すことができます:

String name = person?.organization?.parent?.name

上記の例では、personperson.organization、またはorganization.parentnullの場合、nullが返されます。

6.2. エルビス演算子

エルビス演算子“?:”を使用すると、3項式を凝縮できます。 これら二つは同等です:

String name = person.name ?: defaultName

and

String name = person.name ? person.name : defaultName

どちらも、Groovy true(この場合はnullではなく、non-zeroの長さ)の場合、名前変数にperson.nameの値を割り当てます。

6.3. 宇宙船オペレーター

宇宙船演算子“<⇒”は、JavaのcompareTo()のように機能する関係演算子であり、2つのオブジェクトを比較し、両方の引数の値に応じて-1、0、または+1を返します。

左の引数が右よりも大きい場合、演算子は1を返します。 左の引数が右よりも小さい場合、演算子は-1を返します。 引数が等しい場合、0が返されます。

比較演算子を使用する最大の利点は、x <⇒ yNullPointerExceptionをスローしないように、nullsをスムーズに処理できることです。

println 5 <=> null

上記の例では、結果として1が出力されます。

7. 文字列

文字列リテラルを表現する方法は複数あります。 Javaで使用されるアプローチ(二重引用符で囲まれた文字列)はサポートされていますが、必要に応じて単一引用符を使用することもできます。

他の言語ではヒアドキュメントとも呼ばれる複数行の文字列もサポートされており、トリプルクォート(シングルまたはダブル)を使用します。

他の言語ではヒアドキュメントとも呼ばれる複数行の文字列もサポートされており、トリプルクォート(シングルまたはダブル)を使用します。

二重引用符で定義された文字列は、$\{}構文を使用した補間をサポートします。

def name = "Bill Gates"
def greeting = "Hello, ${name}"

実際、任意の式を$\{}内に配置できます。

def name = "Bill Gates"
def greeting = "Hello, ${name.toUpperCase()}"

二重引用符で囲まれた文字列は、式$\{}が含まれている場合は、GStringと呼ばれます。それ以外の場合は、プレーンなStringオブジェクトです。

以下のコードは、テストに失敗することなく実行されます。

def a = "hello"
assert a.class.name == 'java.lang.String'

def b = 'hello'
assert b.class.name == 'java.lang.String'

def c = "${b}"
assert c.class.name == 'org.codehaus.groovy.runtime.GStringImpl'

8. コレクションと地図

いくつかの基本的なデータ構造がどのように処理されるかを見てみましょう。

8.1. Lists

JavaのArrayListの新しいインスタンスにいくつかの要素を追加するコードを次に示します。

List list = new ArrayList<>();
list.add("Hello");
list.add("World");

そして、これがGroovyでの同じ操作です:

List list = ['Hello', 'World']

リストはデフォルトでタイプjava.util.ArrayListであり、対応するコンストラクターを呼び出すことによって明示的に宣言することもできます。

Setに個別の構文はありませんが、タイプcoercionを使用できます。 どちらでも使えます:

Set greeting = ['Hello', 'World']

or:

def greeting = ['Hello', 'World'] as Set

8.2. Map

Mapの構文は似ていますが、コロンで区切られたキーと値を指定できる必要があるため、少し冗長です。

def key = 'Key3'
def aMap = [
    'Key1': 'Value 1',
    Key2: 'Value 2',
    (key): 'Another value'
]

この初期化の後、エントリKey1 → Value1, Key2 → Value 2, Key3 → Another Valueを持つ新しいLinkedHashMapを取得します。

マップ内のエントリにはさまざまな方法でアクセスできます。

println aMap['Key1']
println aMap[key]
println aMap.Key1

9. 制御構造

9.1. 条件文:if-else

Groovyは、期待どおりに条件付きif/else構文をサポートします。

if (...) {
    // ...
} else if (...) {
    // ...
} else {
    // ...
}

9.2. 条件文:switch-case

switchステートメントはJavaコードと下位互換性があるため、複数の一致に対して同じコードを共有するケースに遭遇する可能性があります。

最も重要な違いは、switchが複数の異なる値型に対してマッチングを実行できることです。

def x = 1.23
def result = ""

switch ( x ) {
    case "foo":
        result = "found foo"
        break

    case "bar":
        result += "bar"
        break

    case [4, 5, 6, 'inList']:
        result = "list"
        break

    case 12..30:
        result = "range"
        break

    case Number:
        result = "number"
        break

    case ~/fo*/:
        result = "foo regex"
        break

    case { it < 0 }: // or { x < 0 }
        result = "negative"
        break

    default:
        result = "default"
}

println(result)

上記の例では、number.が出力されます

9.3. ループ:while

Groovyは、Javaのように通常のwhileループをサポートします。

def x = 0
def y = 5

while ( y-- > 0 ) {
    x++
}

9.4. ループ:for

Groovyはこの単純さを受け入れ、この構造に従うforループを強く推奨します。

for (variable in iterable) { body }

forループはiterableを繰り返します。 よく使用される反復可能オブジェクトは、範囲、コレクション、マップ、配列、反復子、および列挙です。 実際、オブジェクトは反復可能です。

1つのステートメントのみで構成される場合、本体を囲む中括弧はオプションです。 以下は、rangelistarraymap、およびstringsを反復処理する例です。

def x = 0
for ( i in 0..9 ) {
    x += i
}

x = 0
for ( i in [0, 1, 2, 3, 4] ) {
    x += i
}

def array = (0..4).toArray()
x = 0
for ( i in array ) {
    x += i
}

def map = ['abc':1, 'def':2, 'xyz':3]
x = 0
for ( e in map ) {
    x += e.value
}

x = 0
for ( v in map.values() ) {
    x += v
}

def text = "abc"
def list = []
for (c in text) {
    list.add(c)
}

オブジェクトの反復により、Groovyfor-loopは洗練された制御構造になります。 これは、Collection’s eachメソッドを使用するなど、クロージャを使用してオブジェクトを反復処理するメソッドを使用する場合の有効な対応物です。

主な違いは、forループの本体がクロージャではないことです。これは、この本体がブロックであることを意味します。

for (x in 0..9) { println x }

一方、このボディはクロージャーです。

(0..9).each { println it }

見た目は似ていますが、構造が大きく異なります。

クロージャはそれ自体のオブジェクトであり、さまざまな機能があります。 別の場所で作成して、eachメソッドに渡すことができます。 ただし、for-loopの本体は、その出現時にbytecodeとして直接生成されます。 特別なスコープ規則は適用されません。

10. 例外処理

大きな違いは、チェック済み例外処理が強制されないことです。

一般的な例外を処理するために、例外を引き起こす可能性のあるコードをtry/catchブロックに配置できます。

try {
    someActionThatWillThrowAnException()
} catch (e)
    // log the error message, and/or handle in some way
}

キャッチする例外のタイプを宣言しないことにより、すべての例外がここでキャッチされます。

11. クロージャ

簡単に言えば、クロージャーとは、変数に渡すことができ、定義されたコンテキスト内のデータにアクセスできる実行可能コードの匿名ブロックです。

これらは、匿名内部クラスにも似ていますが、インターフェイスを実装したり、基本クラスを拡張したりしません。 Javaのラムダに似ています。

興味深いことに、Groovyはラムダ、特にストリーミングAPIをサポートするために導入されたJDKの追加機能を最大限に活用できます。 ラムダ式が期待される場所では常にクロージャーを使用できます。

以下の例を考えてみましょう。

def helloWorld = {
    println "Hello World"
}

変数helloWorldはクロージャーへの参照を保持するようになり、そのcallメソッドを呼び出すことで実行できます。

helloWorld.call()

Groovyを使用すると、より自然なメソッド呼び出し構文を使用できます。これにより、callメソッドが呼び出されます。

helloWorld()

11.1. パラメーター

メソッドと同様に、クロージャーにはパラメーターを設定できます。 3つのバリアントがあります。

後者の例では、declpersistence_startaredがないため、デフォルト名がitのパラメーターは1つだけです。 送信された内容を出力する変更されたクロージャーは次のようになります。

def printTheParam = { println it }

次のように呼び出すことができます。

printTheParam('hello')
printTheParam 'hello'

また、クロージャー内のパラメーターを期待し、呼び出すときに渡すことができます。

def power = { int x, int y ->
    return Math.pow(x, y)
}
println power(2, 3)

パラメータの型定義は変数と同じです。 型を定義する場合、この型のみを使用できますが、必要なものを渡すこともできます。

def say = { what ->
    println what
}
say "Hello World"

11.2. オプションの返品

クロージャーの最後のステートメントは、returnステートメントを記述する必要なく、暗黙的に返される場合があります。 これを使用して、ボイラープレートコードを最小限に抑えることができます。 したがって、数値の二乗を計算するクロージャーは、次のように短縮できます。

def square = { it * it }
println square(4)

このクロージャは、暗黙パラメータitとオプションのreturnステートメントを使用します。

12. 結論

この記事では、Groovy言語とその主要な機能について簡単に紹介しました。 まず、基本的な構文、条件ステートメント、演算子などの簡単な概念を紹介しました。 また、演算子やクロージャなど、より高度な機能もいくつか示しました。

言語とそのセマンティクスに関する詳細情報が必要な場合は、official siteに直接アクセスできます。