1. 概要

このチュートリアルでは、JVMの世界の新しい言語であるKotlinと、クラス、継承、条件ステートメント、ループ構造などの基本的な機能のいくつかを見ていきます。

次に、nullの安全性、データクラス、拡張関数、 String テンプレートなど、Kotlinを魅力的な言語にする主な機能のいくつかを見ていきます。

2. Mavenの依存関係

MavenプロジェクトでKotlinを使用するには、Kotlin標準ライブラリをpom.xmlに追加する必要があります。

<dependency>
    <groupId>org.jetbrains.kotlin</groupId>
    <artifactId>kotlin-stdlib</artifactId>
    <version>1.0.4</version>
</dependency>

KotlinのJUnitサポートを追加するには、次の依存関係も含める必要があります。

<dependency>
    <groupId>org.jetbrains.kotlin</groupId>
    <artifactId>kotlin-test-junit</artifactId>
    <version>1.0.4</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>

kotlin-stdlib kotlin-test-junit 、およびjunitの最新バージョンはMavenCentralで見つけることができます。

最後に、Mavenビルドを実行するには、ソースディレクトリとKotlinプラグインを構成する必要があります。

<build>
    <sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
    <testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
    <plugins>
        <plugin>
            <artifactId>kotlin-maven-plugin</artifactId>
            <groupId>org.jetbrains.kotlin</groupId>
            <version>1.0.4</version>
            <executions>
                <execution>
                    <id>compile</id>
                    <goals>
                        <goal>compile</goal>
                    </goals>
                </execution>
                <execution>
                    <id>test-compile</id>
                    <goals>
                        <goal>test-compile</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

kotlin-maven-pluginの最新バージョンはMavenCentralにあります。

3. 基本構文

Kotlin言語の基本的な構成要素を見てみましょう。

Javaといくつかの類似点があります(例: パッケージの定義も同じです)。 違いを見てみましょう。

3.1. 関数の定義

Intの戻り型を持つ2つのIntパラメーターを持つ関数を定義しましょう。

fun sum(a: Int, b: Int): Int {
    return a + b
}

3.2. ローカル変数の定義

割り当て1回(読み取り専用)ローカル変数:

val a: Int = 1
val b = 1 
val c: Int 
c = 1

変数bのタイプは、Kotlinコンパイラーによって推測されることに注意してください。 可変変数を定義することもできます。

var x = 5 
x += 1

4. オプションのフィールド

Kotlinには、null許容(オプション)になる可能性のあるフィールドを定義するための基本的な構文があります。 フィールドのタイプがnull許容であることを宣言する場合は、疑問符の接尾辞が付いたタイプを使用する必要があります。

val email: String?

null許容フィールドを定義した場合、nullを割り当てることは完全に有効です。

val email: String? = null

つまり、電子メールフィールドではnullになる可能性があります。次のように記述します。

val email: String = "value"

次に、emailを宣言するのと同じステートメントでemailフィールドに値を割り当てる必要があります。 null値を持つことはできません。 Kotlin null の安全性については、後のセクションで説明します。

5. クラス

製品の特定のカテゴリを管理するための簡単なクラスを作成する方法を示しましょう。 以下のItemManagerクラスには、categoryIddbConnectionの2つのフィールドに入力するデフォルトのコンストラクターと、オプションのemailフィールドがあります。

class ItemManager(val categoryId: String, val dbConnection: String) {
    var email = ""
    // ...
}

そのItemManager(…)構文は、コンストラクターと、クラスにcategoryIddbConnectionの2つのフィールドを作成します。

コンストラクターは引数にvalキーワードを使用することに注意してください。これは、対応するフィールドがfinalで不変であることを意味します。 var キーワードを使用した場合( email フィールドを定義したときのように)、これらのフィールドは変更可能です。

デフォルトのコンストラクターを使用してItemManagerのインスタンスを作成しましょう。

ItemManager("cat_id", "db://connection")

名前付きパラメーターを使用してItemManagerを構築できます。 この例のように、同じタイプの2つのパラメーターを受け取る関数がある場合に非常に便利です。 String であり、それらの順序を混同したくない。 ネーミングパラメータを使用すると、どのパラメータを割り当てるかを明示的に書き込むことができます。 クラスItemManagerには、categoryIddbConnectionの2つのフィールドがあるため、名前付きパラメーターを使用して両方を参照できます。

ItemManager(categoryId = "catId", dbConnection = "db://Connection")

関数にさらに引数を渡す必要がある場合に非常に便利です。

追加のコンストラクターが必要な場合は、コンストラクターキーワードを使用してそれらを定義します。 emailフィールドも設定する別のコンストラクターを定義しましょう。

constructor(categoryId: String, dbConnection: String, email: String) 
  : this(categoryId, dbConnection) {
    this.email = email
}

このコンストラクターは、電子メールフィールドを設定する前に上記で定義したデフォルトのコンストラクターを呼び出すことに注意してください。 また、デフォルトのコンストラクターで val キーワードを使用して、categoryIddbConnectionを不変に定義済みなので、val[を繰り返す必要はありません。追加のコンストラクターのX186X]キーワード。

次に、追加のコンストラクターを使用してインスタンスを作成しましょう。

ItemManager("cat_id", "db://connection", "[email protected]")

ItemManager でインスタンスメソッドを定義する場合は、funキーワードを使用して定義します。

fun isFromSpecificCategory(catId: String): Boolean {
    return categoryId == catId
}

6. 継承

デフォルトでは、Kotlinのクラスは拡張のために閉じられています。これは、Javaでfinalとマークされたクラスに相当します。

クラスが拡張のために開かれていることを指定するには、クラスを定義するときにopenキーワードを使用します。

拡張用に開いているItemクラスを定義しましょう。

open class Item(val id: String, val name: String = "unknown_name") {
    open fun getIdOfItem(): String {
        return id
    }
}

getIdOfItem()メソッドもオープンとして示していることに注意してください。 これにより、オーバーライドできます。

次に、 Item クラスを拡張し、 getIdOfItem()メソッドをオーバーライドします。

class ItemWithCategory(id: String, name: String, val categoryId: String) : Item(id, name) {
    override fun getIdOfItem(): String {
        return id + name
    }
}

7. 条件文

Kotlinでは、条件文ifは何らかの値を返す関数と同等です。 例を見てみましょう:

fun makeAnalyisOfCategory(catId: String): Unit {
    val result = if (catId == "100") "Yes" else "No"
    println(result)
}

この例では、 catId 「100」に等しい条件付きブロックは「はい」を返し、それ以外の場合は「いいえ」を返します戻り値はに割り当てられます結果。

通常のifelseブロックを作成できます。

val number = 2
if (number < 10) {
    println("number less that 10")
} else if (number > 10) {
    println("number is greater that 10")
}

Kotlinには、高度なswitchステートメントとして機能するコマンドが非常に便利なもあります。

val name = "John"
when (name) {
    "John" -> println("Hi man")
    "Alice" -> println("Hi lady")
}

8. コレクション

Kotlinには、可変と不変の2種類のコレクションがあります。 不変のコレクションを作成する場合、それは読み取り専用であることを意味します。

val items = listOf(1, 2, 3, 4)

そのリストには関数の追加要素はありません。

変更可能な可変リストを作成する場合は、 mutableListOf()メソッドを使用する必要があります。

val rwList = mutableListOf(1, 2, 3)
rwList.add(5)

可変リストにはadd()メソッドがあるため、要素を追加できます。 他のタイプのコレクションと同等のメソッドもあります: mutableMapOf()、mapOf()、setOf()、mutableSetOf()

9. 例外

例外処理のメカニズムは、Javaのメカニズムと非常によく似ています。

すべての例外クラスはThrowableを拡張します。例外には、メッセージ、スタックトレース、およびオプションの原因が必要です。 Kotlinのすべての例外はチェックされていません。これは、コンパイラーがそれらをキャッチするように強制しないことを意味します。

例外オブジェクトをスローするには、throw-expressionを使用する必要があります。

throw Exception("msg")

例外の処理は、 try…catchブロック(最終的にオプション)を使用して行われます。

try {

}
catch (e: SomeException) {

}
finally {

}

10. ラムダ

Kotlinでは、ラムダ関数を定義し、それらを引数として他の関数に渡すことができます。

単純なラムダを定義する方法を見てみましょう。

val sumLambda = { a: Int, b: Int -> a + b }

定義しました sumLambda タイプの2つの引数を取る関数 Int 引数として返します Int。

ラムダを渡すことができます:

@Test
fun givenListOfNumber_whenDoingOperationsUsingLambda_shouldReturnProperResult() {
    // given
    val listOfNumbers = listOf(1, 2, 3)

    // when
    val sum = listOfNumbers.reduce { a, b -> a + b }

    // then
    assertEquals(6, sum)
}

11. ループ構造

Kotlinでは、コレクションのループは、標準の for..inconstructを使用して実行できます。

val numbers = arrayOf("first", "second", "third", "fourth")
for (n in numbers) {
    println(n)
}

整数の範囲を反復処理する場合は、範囲構成を使用できます。

for (i in 2..9 step 2) {
    println(i)
}

上記の例の範囲は、両側で包括的であることに注意してください。 step パラメーターはオプションであり、各反復でカウンターを2回インクリメントするのと同じです。 出力は次のようになります。

2
4
6
8

Intクラスで定義されているrangeTo()関数を次のように使用できます。

1.rangeTo(10).map{ it * 2 }

結果には次のものが含まれます( rangeTo()も含まれることに注意してください)。

[2, 4, 6, 8, 10, 12, 14, 16, 18, 20]

12. ヌルセーフティ

Kotlinの重要な機能の1つであるnullセーフティを見てみましょう。これは、言語に組み込まれています。 これが役立つ理由を説明するために、Itemオブジェクトを返す単純なサービスを作成します。

class ItemService {
    fun findItemNameForId(id: String): Item? {
        val itemId = UUID.randomUUID().toString()
        return Item(itemId, "name-$itemId");
    }
}

注意すべき重要なことは、そのメソッドの戻り型です。 オブジェクトの後に疑問符が続きます。 これはKotlin言語からの構成であり、そのメソッドから返されるItemがnullになる可能性があることを意味します。 コンパイル時にそのケースを処理し、そのオブジェクトで何をしたいかを決定する必要があります(Java8とほぼ同等です)。 オプションタイプ)。

メソッドシグネチャに疑問符のないタイプがある場合:

fun findItemNameForId(id: String): Item

コンパイラとKotlin言語によって保証されているため、呼び出し元のコードはnullの場合を処理する必要はなく、返されたオブジェクトをnullにすることはできません。

それ以外の場合、メソッドに渡されたnull許容オブジェクトがあり、そのケースが処理されない場合、コンパイルされません。

Kotlinの型安全性のテストケースを書いてみましょう。

val id = "item_id"
val itemService = ItemService()

val result = itemService.findItemNameForId(id)

assertNotNull(result?.let { it -> it.id })
assertNotNull(result!!.id)

ここでは、メソッド findItemNameForId()を実行した後、返されるタイプがKotlin Nullableであることがわかります。 そのオブジェクトのフィールド( id )にアクセスするには、コンパイル時にそのケースを処理する必要があります。 メソッドlet()は、結果がnull許容でない場合にのみ実行されます。 I d フィールドはnullセーフであるため、ラムダ関数内でアクセスできます。

null許容オブジェクトフィールドにアクセスする別の方法は、Kotlin演算子!!。を使用することです。これは次と同等です。

if (result == null){
    throwNpe(); 
}
return result;

Kotlinは、そのオブジェクトが null であるかどうかを確認し、そうである場合は NullPointerException、をスローし、そうでない場合は適切なオブジェクトを返します。 関数throwNpe()はKotlinの内部関数です。

13. データクラス

Kotlinで見つけることができる非常に優れた言語構造は、データクラスです(Scala言語の「ケースクラス」に相当します)。 このようなクラスの目的は、データのみを保持することです。 この例では、データのみを保持するItemクラスがあります。

data class Item(val id: String, val name: String)

コンパイラは、メソッド hashCode() equals()、および toString()を作成します。 val キーワードを使用して、データクラスを不変にすることをお勧めします。 データクラスにはデフォルトのフィールド値を設定できます。

data class Item(val id: String, val name: String = "unknown_name")

nameフィールドのデフォルト値は「unknown_name」であることがわかります。

14. 拡張機能

サードパーティライブラリの一部であるクラスがあるが、追加のメソッドでそれを拡張したいとします。 Kotlinでは、拡張機能を使用してこれを行うことができます。

要素のリストがあり、そのリストからランダムな要素を取得したい例を考えてみましょう。 新しい関数random()をサードパーティのListクラスに追加します。

Kotlinでは次のようになります。

fun <T> List<T>.random(): T? {
    if (this.isEmpty()) return null
    return get(ThreadLocalRandom.current().nextInt(count()))
}

ここで注意すべき最も重要なことは、メソッドのシグネチャです。 メソッドの前には、この追加のメソッドを追加するクラスの名前が付けられます。

拡張メソッド内では、リストのスコープを操作するため、 これ次のようなリストインスタンスメソッドへのアクセスを使用できるようにしました isEmpty() またカウント()。 その後、私たちは呼び出すことができますランダム() そのスコープ内にあるリストのメソッド:

fun <T> getRandomElementOfList(list: List<T>): T? {
    return list.random()
}

リストを取得して、以前に定義したカスタム拡張関数 random()を実行するメソッドを作成しました。 新しい関数のテストケースを書いてみましょう。

val elements = listOf("a", "b", "c")

val result = ListExtension().getRandomElementOfList(elements)

assertTrue(elements.contains(result))

サードパーティのクラスを「拡張」する関数を定義する可能性は非常に強力な機能であり、コードをより簡潔で読みやすくすることができます。

15. 文字列テンプレート

Kotlin言語の非常に優れた機能は、Stringのテンプレートを使用できることです。 String を手動で連結する必要がないため、非常に便利です。

val firstName = "Tom"
val secondName = "Mary"
val concatOfNames = "$firstName + $secondName"
val sum = "four: ${2 + 2}"

${}ブロック内の式を評価することもできます。

val itemManager = ItemManager("cat_id", "db://connection")
val result = "function result: ${itemManager.isFromSpecificCategory("1")}"

16. Kotlin/Javaの相互運用性

Kotlin –Javaの相互運用性はシームレスに簡単です。 String:を操作するメソッドを持つJavaクラスがあるとします。

class StringUtils{
    public static String toUpperCase(String name) {
        return name.toUpperCase();
    }
}

次に、Kotlinクラスからそのコードを実行します。 そのクラスをインポートするだけで、Kotlinからjavaメソッドを問題なく実行できます。

val name = "tom"

val res = StringUtils.toUpperCase(name)

assertEquals(res, "TOM")

ご覧のとおり、Kotlinコードのjavaメソッドを使用しました。

JavaからKotlinコードを呼び出すことも非常に簡単です。 簡単なKotlin関数を定義しましょう:

class MathematicsOperations {
    fun addTwoNumbers(a: Int, b: Int): Int {
        return a + b
    }
}

JavaコードからaddTwoNumbers()を実行するのは非常に簡単です。

int res = new MathematicsOperations().addTwoNumbers(2, 4);

assertEquals(6, res);

Kotlinコードへの呼び出しは透過的であることがわかります。

Javaで戻り型がvoidであるメソッドを定義すると、Kotlinでは戻り値はUnit型になります。

Java言語にはいくつかの特別な識別子( is object in 、..)があり、Kotlinコードで使用する場合はエスケープする必要があります。 たとえば、 object()という名前のメソッドを定義できますが、これはJavaの特別な識別子であるため、その名前をエスケープすることを忘れないでください。

fun `object`(): String {
    return "this is object"
}

次に、そのメソッドを実行できます。

`object`()

17. 結論

この記事では、Kotlin言語とその主な機能を紹介します。 まず、ループ、条件ステートメント、クラスの定義などの単純な概念を紹介します。 次に、拡張機能やnullの安全性などのより高度な機能を示します。

これらすべての例とコードスニペットの実装は、GitHubプロジェクトにあります。