サイトロゴ

Enjoy Creating
Web & Mobile Apps

MENU BOX
WEB
MOBILE
OPEN

ホーム

 > 

 > 

【Kotlin】GenericsとAnyの重要な違いの一つ

【Kotlin】GenericsとAnyの重要な違いの一つ

この記事にはプロモーションが含まれています。

Kotlinには様々なデータの型(タイプ)が用意されていますが、特定の型に限定したくない場合には、GenericsAny を利用することができます。

GenericsもAnyも、どちらも様々なデータの型を受け入れられるという点では似ていますが、実はこの両者には重要な違いがあります。

今回はその違いの中でも、特に重要で、その違いが分かりやすいものをご紹介します!

この記事を読むことで分かること
  • ・Genericsの基本
  • ・Anyの基本
  • ・GenericsとAnyの重要な違い

– 目次 –

Genericsの一般的な使用例

まず、「そもそもGenericsって何?」という方のために、一般的なGenericsの使用例を確認しておきたいと思います。

Genericsは、『一般的』という意味の英単語(Generic)からも想像できるように、汎用的な型を扱うclassなどを定義したいときに使われるものです。

ちなみに、このGenericsという仕組み・機能は、Kotlinに限らず、多くのプログラミング言語に備わっています。

たとえば、データの型を限定せずに何らかの値をコンストラクタの引数として受け取り、その値の情報を出力するメソッドを備えたclass(GenericsEx)は、次のように定義することができます。

Kotlin

class GenericsEx<T> (private val arg: T) {
    fun printArg() {
        println(arg)
    }
}

fun main() {
    val intGenericsEx = GenericsEx<Int>(32)
    intGenericsEx.printArg() // Outputs: 32
}

上の例では、整数(Int)を渡していますが、文字列(String)や真偽値(Boolean)などを渡すこともできます。

Kotlin

class GenericsEx<T> (private val arg: T) {
    fun printArg() {
        println(arg)
    }
}

fun main() {
    val stringGenericsEx = GenericsEx<String>("hello")
    stringGenericsEx.printArg() // Outputs: hello
    
    val booleanGenericsEx = GenericsEx<Boolean>(true)
    booleanGenericsEx.printArg() // Outputs: true
}

このように、Genericsを利用して定義することで、データの型を限定しない汎用的なclassを作成することが可能です。

    POINT!
  1. ・Genericsはデータの型を特定せず、汎用的なclassを定義したい場合などに使われる!
  2. ・Genericsを利用して定義することで、Int,Boolean,Stringなど様々な型のデータを受け入れられる!

似たことはAnyを使ってもできる

前章で、『Genericsは、データの型を限定しない場合に使用する』という説明をしましたが、実はGenerics以外にも、多くのデータ型を受け付けるようにする方法はあります。

その一つが、null以外のデータ型を受け付ける型である Any を利用するという方法です。

前章で紹介したclassとほぼ同じ役割を果たすclassを、Anyを使って表現すると次のようになります。

Kotlin

class AnyEx (private val arg: Any) {
    fun printArg() {
        println(arg)
    }
}

fun main() {
    val intAnyEx = AnyEx(16)
    intAnyEx.printArg() // Outputs: 16
    
    val stringAnyEx = AnyEx("hi")
    stringAnyEx.printArg() // Outputs: hi
    
    val booleanAnyEx = AnyEx(false)
    booleanAnyEx.printArg() // Outputs: false
}

Genericsを利用した場合と同じく、様々な型のデータを渡せることが確認できますね。

となると、GenericsとAnyの違いがなんだかよく分からなくなってきませんか?

そこで、classにプロパティを一つ追加して、両者の決定的な違い(の一つ)を明らかにしてみようと思います。

    POINT!
  1. ・Anyを利用することでも、様々なデータの型を受け付けられる!
  2. ・この時点では、Genericsとの違いが分かりづらいが…!?

Genericsは型情報を保持する

ただ単に、コンストラクタに渡した引数を出力するメソッドを有するだけであれば、GenericsとAnyの違いがよく分かりませんでしたが、コンストラクタに渡した引数をプロパティとして利用することで、その違いを鮮明にできます。

結論から言うと、Genericsではコンストラクタに渡されたデータの型情報を保持するため、データの型に応じて、その型専用のプロパティやメソッドなどをそのまま利用することができます。

たとえば、Genericsとして定義されているコンストラクタのプロパティ(ここではarg)にInt型を渡した場合、argプロパティはその時点でInt型であることが確定し、その後もInt型であるという情報を保持します。

そのため、if文などを用いて条件判定せずとも、Float型に変換する toFloat() メソッドなどを適用することができます。

以下はその例を示すコードです。

Kotlin

class GenericsEx<T> (private val arg: T) {
    fun printArg() {
        println(arg)
    }
    val prop = this.arg
}

fun main() {
    val intGenericsEx = GenericsEx<Int>(32)
    val floatValue = intGenericsEx.prop.toFloat() // ..1
    println(floatValue) // Outputs: 32.0
}

上記のコードの ..1 のラインで、intGenericsEx インスタンスの prop プロパティに対し、そのまま toFloat() メソッドを実行できていることが確認できます。

これは、arg を介して定義された prop プロパティがInt型の情報を保持しているからです。

一方、Anyを使った場合、型情報が保持されないため、同じことをしようとするとエラーになります。

Kotlin

class AnyEx (private val arg: Any) {
    fun printArg() {
        println(arg)
    }
    val prop = this.arg
}

fun main() {
    val intAnyEx = AnyEx(16)
    val floatValue = intAnyEx.prop.toFloat() // This code happens an error
    println(floatValue) // don't work
}

Anyの場合、条件分岐などを使って型情報をチェックする必要があります。

コードを下のように修正し、条件分岐により prop がInt型であることを確定させれば、proptoFloat() でFloat型にした結果を得ることができます。

Kotlin

class AnyEx (private val arg: Any) {
    fun printArg() {
        println(arg)
    }
    val prop = this.arg
}

fun main() {
    val intAnyEx = AnyEx(16)    
    if(intAnyEx.prop is Int) {
        val floatValue = intAnyEx.prop.toFloat() // This is OK
        println(floatValue) // Outputs: 16.0
    }
}

このように、『型情報を保持するか or しないか』が、GenericsとAnyの重要な違いの一つです。

なので、型情報を保持する必要があったり、型情報が保持された方が都合が良い場合は、Genericsを採用した方が良いと言えます。

一方で、単発のメソッドなど、使い切りで使用されるようなケース(型情報の保持が重要でない場合)では、Anyの方がシンプルで分かりやすいかもしれません。

GenericsとAnyのうち、どちらを使った方が良い…という『正解』が常にあるわけではなく、開発者や開発チームの意図・思想・設計方針などによって左右されるものです。

大切なのは、違いをきちんと理解した上で使い分けることではないかと思います。

    POINT!
  1. ・Genericsでは型情報が保持される!
  2. ・Anyでは型情報が保持されない!
  3. ・型専用のプロパティやメソッドをそのまま利用したい場合は、Genericsの方が便利!

« »

カテゴリーリンク

著者について- author profile -

ROYDOプロフィール写真
Michihiro

モバイルアプリ(iOS・Android)ディベロッパー&デザイナー

これまでに、可読性の高いカラーパターンを自動で生成するアプリや、『第3火曜日』といった形式で通知をスケジュールできるアプリなどを制作。

サブでWebデザイン・フロントエンドエンジニアとしても活動しています。

📝ツール・言語:JavaScript/React Native/Kotlin/Android Studio/Swift/SwiftUI

🎓資格:基本情報技術者/ウェブデザイン技能検定3級

Twitterアイコン Instagramアイコン