Kotlinコンストラクタの15選!初心者でもわかる徹底解説!

Kotlinのコンストラクタを図解しながら徹底解説するイメージKotlin
この記事は約19分で読めます。

 

【サイト内のコードはご自由に個人利用・商用利用いただけます】

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

基本的な知識があればカスタムコードを使って機能追加、目的を達成できるように作ってあります。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を凌駕する現役のプログラマチームによって監修されています。

サイト内のコードを共有する場合は、参照元として引用して下さいますと幸いです

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

はじめに

この記事を読めば、Kotlinのコンストラクタを使いこなすことができるようになります。

Kotlinは、近年のAndroidアプリ開発で主流となってきたプログラミング言語です。

その魅力として「簡潔な文法」「強力な型推論」「Javaとの高い互換性」などが挙げられます。

特に、コンストラクタに関しては、Javaとは異なる独自の特性を持っています。

そこで、この記事ではKotlinのコンストラクタの基本から、応用的な使い方まで、15のサンプルコードを交えながら初心者目線で詳しく解説していきます。

●Kotlinとは?

Kotlinは、JetBrains社が開発した、静的型付けのプログラム言語です。

Javaの代替として開発されたこの言語は、Javaの持つ冗長な部分を削除し、より簡潔かつ安全にコードを書くことができるように設計されています。

Androidアプリ開発の公式言語として採用されて以降、その人気は急上昇しました。

○Kotlinの特徴とメリット

Kotlinの最大の特徴は、Javaとの100%の互換性を持つことです。

これにより、既存のJavaコードとKotlinコードを同じプロジェクト内で混在させることが可能となりました。

また、KotlinはNull安全を重視した設計となっており、NullPointerExeptionというJavaプログラマーの大敵を大幅に減少させることが可能です。

また、Kotlinの文法は非常に簡潔であり、より少ないコード量で同じロジックを実現できます。

特に、データクラスや拡張関数などの機能は、実際の開発で非常に役立ちます。

Kotlinのメリットをいくつか挙げると、次のようになります。

  1. 簡潔な文法:コードが読みやすく、書きやすい。
  2. Null安全:安全にNullを扱うことができる。
  3. Javaとの互換性:既存のJavaコードとの連携が容易。
  4. スマートキャスト:型チェック後に自動的にキャストしてくれる。
  5. 関数型プログラミング:より効率的なコーディングが可能。

これらの特徴とメリットを踏まえた上で、次の章ではKotlinのコンストラクタの基本について詳しく解説していきます。

コンストラクタは、オブジェクトを生成する際の初期設定を行うための重要な要素です。

Kotlinにおけるコンストラクタの独自の特性や使い方をしっかりと理解して、効率的なコードを書いていきましょう。

●Kotlinのコンストラクタの基本

コンストラクタは、オブジェクトの初期化処理を担当するもので、クラスがインスタンス化される際に呼び出されます。

Kotlinでは、プライマリコンストラクタとセカンダリコンストラクタという、2種類のコンストラクタが存在します。

それぞれの使い方や特性について、順を追って解説していきます。

○プライマリコンストラクタ

Kotlinのプライマリコンストラクタは、クラスヘッダ部分に宣言され、そのクラスのメインなコンストラクタとなります。

シンプルな構造のクラスを作成する場合には、プライマリコンストラクタのみを使用することが多いです。

このコードでは、Personクラスのプライマリコンストラクタを定義しています。

このコンストラクタは、nameとageという2つの引数を持ちます。

class Person(val name: String, var age: Int)

このコードを実行すると、Personクラスのインスタンスを作成し、nameとageの値を初期化できます。

○セカンダリコンストラクタ

セカンダリコンストラクタは、クラス内部にconstructorキーワードを使用して定義されるもので、複数の異なる初期化処理を持つ場合や、特定の処理を伴う場合に利用します。

このコードでは、Personクラスにセカンダリコンストラクタを追加しています。

このコンストラクタは、nameという1つの引数を持ち、ageの値を20で初期化します。

class Person(val name: String, var age: Int) {
    constructor(name: String) : this(name, 20)
}

このコードを実行すると、Personクラスのインスタンスをnameのみの引数で作成すると、ageの初期値が20になります。

これらのプライマリコンストラクタとセカンダリコンストラクタをうまく組み合わせることで、柔軟な初期化処理を実現することができます。

特に、セカンダリコンストラクタは複数定義することも可能なので、様々な条件や状況に合わせた初期化を行いたい場合に非常に役立ちます。

●コンストラクタの詳細な使い方

Kotlinのコンストラクタの使い方は、他のプログラミング言語と比較しても、非常に簡潔でありながら強力です。

ここでは、その詳細な使い方をサンプルコードと共に、初心者の方にもわかりやすく解説していきます。

○サンプルコード1:プライマリコンストラクタの基本形

Kotlinのプライマリコンストラクタは、クラス宣言と同時に書かれます。

下記のコードは、プライマリコンストラクタを使用して、nameとageという2つのプロパティを持つPersonクラスを表しています。

class Person(val name: String, var age: Int)

このコードでは、nameはvalで宣言されているため、読み取り専用のプロパティとして扱われます。

一方、ageはvarで宣言されているため、値の変更が可能です。

○サンプルコード2:セカンダリコンストラクタの使用例

セカンダリコンストラクタを使用する場合、constructorキーワードを使用します。

下記のサンプルコードは、セカンダリコンストラクタを使用して、名前のみを指定してインスタンスを作成できるPersonクラスを表しています。

class Person(val name: String, var age: Int) {
    constructor(name: String) : this(name, 20)
}

このコードでは、セカンダリコンストラクタでnameのみを受け取り、ageの初期値として20を設定しています。

○サンプルコード3:初期化ブロックを伴うコンストラクタ

Kotlinでは、コンストラクタ内で追加の初期化処理を行いたい場合、initブロックを使用します。

下記のコードは、プライマリコンストラクタの後に初期化ブロックを使用して、特定の条件下でのエラーチェックを行うPersonクラスの例を示しています。

class Person(val name: String, var age: Int) {
    init {
        if (age < 0) {
            throw IllegalArgumentException("年齢は0以上でなければなりません。")
        }
    }
}

このコードでは、年齢が0未満の場合に例外をスローしています。

このようにして、コンストラクタでの初期値の検証や、追加の初期化処理を行うことができます。

●コンストラクタの応用例

Kotlinのコンストラクタは多様な応用が可能です。

シンプルな宣言から始めて、より複雑な構造や特定の条件に合わせたカスタマイズが求められる場合、さまざまなテクニックを駆使することができます。

○サンプルコード4:データクラスとプライマリコンストラクタ

Kotlinのデータクラスは、データを保持するためのクラスを簡潔に宣言するための特別なクラスです。

データクラスには、プライマリコンストラクタを使用して、プロパティを宣言する必要があります。

data class Book(val title: String, val author: String, val price: Int)

このコードを実行すると、title, author, priceというプロパティを持つBookデータクラスが作成されます。

データクラスは、equals(), hashCode(), toString()などの関数が自動的に生成されるので、手間なく使うことができます。

○サンプルコード5:セカンダリコンストラクタでのオーバーロード

Kotlinでは、セカンダリコンストラクタを使用して、コンストラクタのオーバーロードが可能です。

下記のサンプルコードでは、異なるパラメータを受け取る2つのセカンダリコンストラクタを持つStudentクラスを表しています。

class Student(val id: Int) {
    var name: String = "N/A"
    var age: Int = 0

    constructor(id: Int, name: String) : this(id) {
        this.name = name
    }

    constructor(id: Int, age: Int) : this(id) {
        this.age = age
    }
}

このコードでは、idというプロパティを持つStudentクラスが宣言され、名前か年齢を追加で設定できる2つのセカンダリコンストラクタが定義されています。

これにより、Studentクラスのインスタンスを作成する際に、名前や年齢をオプションとして指定することができます。

○サンプルコード6:拡張関数とコンストラクタの組み合わせ

KotlinはJavaよりも表現力が豊かで、その特性の一つとして「拡張関数」が挙げられます。

拡張関数を用いると、既存のクラスに新しいメソッドを追加することができます。

ここでは、拡張関数とコンストラクタを組み合わせた応用例をご紹介します。

例として、Stringクラスに新しいコンストラクタを追加する場合を考えてみましょう。

この新しいコンストラクタは、指定した回数だけ文字列を繰り返す機能を持つとします。

fun String.repeatString(times: Int): String {
    var result = ""
    for (i in 1..times) {
        result += this
    }
    return result
}

// 拡張関数の利用
val repeatedString = "Hello".repeatString(3)
println(repeatedString)  // 出力結果: HelloHelloHello

上記のコードでは、拡張関数repeatStringをStringクラスに追加しています。

この関数を利用することで、文字列を指定した回数だけ繰り返すことができます。

結果として、”Hello”という文字列を3回繰り返した”HelloHelloHello”が得られます。

○サンプルコード7:ジェネリクスを伴うコンストラクタの使い方

Kotlinのジェネリクスは、型の柔軟性と安全性を向上させるための重要な機能の一つです。

ジェネリクスを使用したコンストラクタの定義方法について詳しく見ていきましょう。

例として、異なる型のオブジェクトをペアにして保持するPairクラスを考えます。

このクラスは2つのジェネリクス型TUを持つとします。

class Pair<T, U>(val first: T, val second: U)

val intStringPair = Pair(1, "One")
val doubleBoolPair = Pair(1.5, true)

上記のコードでは、ジェネリクスを用いたPairクラスを定義しています。

このクラスは2つの異なる型のオブジェクトをペアにして保持します。

コンストラクタにはジェネリクス型のパラメータが使用されており、インスタンス生成時に具体的な型を指定することができます。

実際に、intStringPairという変数にはInt型とString型のペアが、doubleBoolPairという変数にはDouble型とBoolean型のペアがそれぞれ格納されています。

これにより、異なる型のオブジェクトを一緒に扱うことが容易になります。

●コンストラクタの注意点と対処法

Kotlinのコンストラクタを使用する際、留意すべき点とそれに対する対処法を取り上げていきます。

正しく理解し、トラブルを避けるためのヒントとして、次の内容を参考にしてください。

○サンプルコード8:コンストラクタでのバリデーション例

Kotlinのコンストラクタ内での値のチェックは、データの整合性を保つために非常に重要です。

下記のサンプルコードは、コンストラクタ内でのバリデーションの基本的な手法を表しています。

class Person(val name: String, age: Int) {
    var age = age
        set(value) {
            if (value < 0) throw IllegalArgumentException("年齢は0以上である必要があります。")
            field = value
        }

    init {
        if (name.isBlank()) throw IllegalArgumentException("名前は空白ではいけません。")
        if (age < 0) throw IllegalArgumentException("年齢は0以上である必要があります。")
    }
}

// 使用例
try {
    val person = Person("山田太郎", -5)
} catch (e: IllegalArgumentException) {
    println(e.message)
}

上記のコードでは、Personクラスのコンストラクタ内とプロパティのセッターで、名前と年齢のバリデーションを行っています。

不正な値が入力された場合は、例外を投げています。

このコードを実行すると、不正な年齢のエラーメッセージが出力されます。

○サンプルコード9:セカンダリコンストラクタの呼び出し順序

Kotlinのクラスには、プライマリコンストラクタとセカンダリコンストラクタの両方が存在することがあります。

この場合、セカンダリコンストラクタがプライマリコンストラクタを呼び出す順序を理解しておくことが重要です。

class Rectangle {
    var width: Int
    var height: Int

    constructor(width: Int, height: Int) {
        this.width = width
        this.height = height
        println("セカンダリコンストラクタが呼び出されました。")
    }

    init {
        println("初期化ブロックが実行されました。")
    }
}

// 使用例
val rectangle = Rectangle(10, 20)

このコードを実行すると、初期化ブロックのメッセージが先に、次にセカンダリコンストラクタのメッセージが出力されます。

これは、セカンダリコンストラクタがプライマリコンストラクタ(またはinitブロック)を先に呼び出すためです。

●コンストラクタのカスタマイズ方法

Kotlinのコンストラクタは柔軟にカスタマイズすることが可能です。

ここでは、コンストラクタのカスタマイズの方法をいくつかのサンプルコードとともに解説します。

○サンプルコード10:カスタムセッターを持つコンストラクタ

コンストラクタの引数に直接カスタムセッターを追加することで、プロパティの設定時に特定の処理を挟むことができます。

下記のサンプルコードでは、年齢を設定する際に0以上であることを確認するカスタムセッターを持つコンストラクタを表しています。

class User(name: String, age: Int) {
    var name = name
    var age = age
        set(value) {
            if (value < 0) {
                println("年齢は0以上を設定してください。")
            } else {
                field = value
            }
        }
}

val user = User("鈴木", -1)
println(user.age)  // 年齢は0以上を設定してください。が出力され、ageは変更されない。

このコードでは、年齢が0未満の場合に警告を出力し、ageの値は変更されません。

○サンプルコード11:コンストラクタを持たないオブジェクト宣言

Kotlinでは、単一のインスタンスしか存在しないオブジェクトを宣言することができます。これは、Javaのシングルトンパターンに相当します。

下記のサンプルコードでは、コンストラクタを持たないオブジェクト宣言を表しています。

object Singleton {
    val message = "これはシングルトンオブジェクトです。"
}

println(Singleton.message)  // これはシングルトンオブジェクトです。が出力される。

このコードを実行すると、オブジェクトのメッセージが出力されます。

オブジェクト宣言にはコンストラクタを持つことができませんが、初期化処理などはinitブロックを使用して実装することが可能です。

○サンプルコード12:コンパニオンオブジェクトとコンストラクタ

Kotlinでは、Javaのstaticメソッドやフィールドに相当するものとして、コンパニオンオブジェクトを使用することができます。

コンパニオンオブジェクト内には、インスタンスを生成せずに呼び出せるメソッドやプロパティを定義できます。

特に、ファクトリーメソッドのような特定のコンストラクタを簡単に提供したい場合に有効です。

下記のサンプルコードでは、コンパニオンオブジェクトを使用して特定の初期条件を持つインスタンスを生成する方法を表しています。

class Person(val name: String, val age: Int) {
    companion object {
        fun child(name: String): Person {
            return Person(name, 0)
        }

        fun adult(name: String): Person {
            return Person(name, 20)
        }
    }
}

val child = Person.child("田中")
val adult = Person.adult("山田")

println("${child.name}は${child.age}歳です。")  // 田中は0歳です。
println("${adult.name}は${adult.age}歳です。")  // 山田は20歳です。

このコードでは、child関数を使って0歳の田中さん、adult関数を使って20歳の山田さんのインスタンスを生成しています。

○サンプルコード13:非公開コンストラクタの利用

Kotlinでは、特定の条件下でのみインスタンスを生成したい場合、コンストラクタを非公開(private)にすることができます。

非公開のコンストラクタを持つクラスを定義することで、外部からの不要なインスタンス生成を防ぐことができます。

下記のサンプルコードでは、非公開のコンストラクタを持つクラスと、そのクラスのファクトリーメソッドを表しています。

class Secret private constructor(val secretCode: String) {
    companion object {
        fun create(): Secret {
            return Secret("秘密のコード")
        }
    }
}

val secretInstance = Secret.create()
println(secretInstance.secretCode)  // 秘密のコードが出力される。

このコードでは、Secretクラスのコンストラクタは非公開となっているため、外部から直接インスタンスを生成することはできません。

代わりに、コンパニオンオブジェクト内のcreateメソッドを使用してインスタンスを生成しています。

○サンプルコード14:デリゲートを使用したコンストラクタのカスタマイズ

Kotlinでは、デリゲートパターンをシンプルに実装するためのデリゲートプロパティが用意されています。

特にコンストラクタのカスタマイズにおいても、デリゲートを活用することで、複雑なロジックをシンプルに保つことができます。

下記のサンプルコードは、デリゲートを使用してコンストラクタ内のプロパティの値を制御する方法を表しています。

class LazySample {
    val lazyValue: String by lazy {
        println("計算中")
        "Hello"
    }
}

fun main() {
    val sample = LazySample()
    println("LazySampleクラスのインスタンスが生成されました。")
    println(sample.lazyValue)
    println(sample.lazyValue)
}

このコードでは、lazyデリゲートを使ってlazyValueプロパティの初期化を遅延させています。

main関数内でLazySampleクラスのインスタンスを生成した際、lazyValueの初期化は行われません。

しかし、初めてlazyValueにアクセスすると、その時点で初期化が行われます。

実際の出力結果は次の通りです。

LazySampleクラスのインスタンスが生成されました。
計算中
Hello
Hello

ここで注目すべきは、lazyValueに2回アクセスしているにも関わらず、「計算中」という文字列が1回しか出力されていない点です。

これは、lazyデリゲートが初めてアクセスされた際にだけ計算を行い、2回目以降は計算済みの値を返すためです。

○サンプルコード15:アノテーションを使ったコンストラクタの修飾

Kotlinでは、アノテーションを使用してコンストラクタを修飾することができます。

特に、依存性の注入やフレームワークとの連携などで役立つ場面があります。

下記のサンプルコードは、アノテーションを使用してコンストラクタを修飾する方法を表しています。

annotation class Inject

class UserController @Inject constructor(private val userRepository: UserRepository) {
    fun getUser(): User {
        return userRepository.findUser()
    }
}

class UserRepository {
    fun findUser(): User {
        return User("Taro", 25)
    }
}

data class User(val name: String, val age: Int)

このコードでは、UserControllerクラスのコンストラクタに@Injectアノテーションを付与しています。

このアノテーションは、実際のアプリケーション開発において、依存性注入フレームワークなどと連携して、必要な依存オブジェクトを自動的に注入するためのものとして使われることが多いです。

まとめ

Kotlinは、Javaに比べてシンタックスが簡潔でありながら、非常に強力な機能を持つプログラミング言語です。

本記事で取り上げたコンストラクタに関連する機能や技法は、Kotlinの柔軟さと強力さの一例に過ぎません。

特に、コンストラクタのカスタマイズやアノテーションを用いた修飾など、日常のアプリケーション開発で役立つテクニックをいくつか学びました。

また、デリゲートを利用したプロパティの初期化や、アノテーションを活用したコンストラクタの修飾など、Kotlin独自の機能を効果的に利用することで、より効率的で安全なコードを書くことができます。

Kotlin初心者の方にとって、これらの機能やテクニックは一見複雑に思えるかもしれませんが、理解して使いこなすことで、アプリケーション開発の幅が大きく広がります。

今回の記事を通じて、Kotlinのコンストラクタに関する知識を深めることができたことを願っています。