kotlin基本構文-データクラス,シングルトンオブジェクト,オブジェクト式,SAM変換,コンパニオンオブジェクト,Enumクラス

データクラスの同値性

構造体のような用途で使うデータクラス。
プライマリコンストラクタだけで目的を達成できるからclassブロックがない。


data class Article(val url: String, val title: String)
var article1 = Article("http://ikuty.com/1","記事1")
var article2 = Article("http://ikuty.com/2","記事2")

インスタンスの同値性は「==」演算子で評価される。
データクラスには「equals」が定義済みで「==」評価時にequalsが呼ばれる。
データクラスのインスタンス同士を「==」演算子で比較すると、
プライマリコンストラクタで指定した値が同じ場合に限りtrueが返る。


data class Article(val url: String, val title: String)
var article1 = Article("http://ikuty.com/1","記事1")
var article2 = Article("http://ikuty.com/1","記事1")
var article3 = Article("http://ikuty.com/2","記事2")
println(article1 == article2) // true
println(article1 == article3) // false

データクラスの同値性はプライマリコンストラクタで定義された値だけが対象となる。
データクラスに含まれる他のメンバ変数が異なっていても、プライマリコンストラクタで
定義された値が同じであればtrueを返す。


data class Article(val url: String, val title: String)
{
    var value: Int = 0
}
var article1 = Article("http://ikuty.com/1","記事1")
var article2 = Article("http://ikuty.com/1","記事1")

article1.value = 100
article2.value = 100
println(article1 == article2)

article1.value = 100
article2.value = 200
println(article1 == article2)

データクラスのtoString

インスタンスの文字列化は「toString」関数が評価される。
例えば以下のような感じ。
プライマリコンストラクタで渡さなかったメンバ変数は出てこない。


data class Article(val url: String, val title: String)
{
    var value: Int = 0
}
var article1 = Article("http://ikuty.com/1","記事1")
println(article1) // Article(url=http://ikuty.com/1, title=記事1)

データクラスのプロパティの分割

例えば、IntのPairを2つのIntに分割する書式は以下だった。


val pair :Pair = Pair(10,20)
val (p1, p2) = pair
println(p1) // 10
println(p2) // 20

この書式において、Pairクラスのcomponent2メソッドが評価されている。
一般にcomponentN関数により左オペランドのN個の変数に代入する。
Pairの場合はcomponent2が評価され、左オペランドの2個の引数に変数が代入されている。

データクラスにおいても予めcomponentNが定義されており、
左オペランドにプライマリコンストラクタで指定した値が代入される。


data class Article(val url: String, val title: String)
{
    var value: Int = 0
}
var article1 = Article("http://ikuty.com/1","記事1")

val (v1, v2) = article1
println(v1) // http://ikuty.com/1
println(v2) // 記事1

データクラスの複製

copyメソッドによりインスタンスを複製できる。
その際、名前付き引数で指定した値を変更してから複製することもできる。


data class Article(val url: String, val title: String)
{
    var value: Int = 0
}
var article1 = Article("http://ikuty.com/1","記事1")
var article2 = article1.copy()
var article3 = article1.copy(title="記事2")
println(article1) // Article(url=http://ikuty.com/1, title=記事1)
println(article2) // Article(url=http://ikuty.com/1, title=記事1)
println(article3) // Article(url=http://ikuty.com/1, title=記事2)

シングルトンオブジェクト

クラスではなくオブジェクトそのものを定義し、そのオブジェクトを使い回す書式。
以下のように書く。


object MySingletonSettings
{
    var name: String = "hoge"
}
fun main(args: Array) {

    println(MySingletonSettings.name)
}

オブジェクト式

クラス定義を書かないで1回限りのオブジェクトを定義するやり方。
Androidアプリを書くときに、ボタンのイベントリスナーにオブジェクトを渡す例のアレ。

以下のように書く。

btnのsetOnClickListenerにobjectという名称のオブジェクトを渡している。
objectはView.OnClickListenerインターフェースを実装していて、
onClickメソッドをオーバーライドしている。
Androidアプリを書くとコレでいっぱいになると思われる。


btn.setOnClickListener(object: View.OnClickListener{
	override fun onClick(view: View) {
	  Log.v("Hello Kotlin","clicked!")
	}
})

SAM変換

オブジェクト式で実装するインターフェースがもつ抽象メソッドが1つ
(Single Abstract Method)の場合、
overrideが必要なメソッドは1つしかないということなので、
ラムダ式でそのメソッドを書くことができる。
以下のように書く。


btn.setOnClickListener({ view: View -> Log.v("Hello Kotlin","clicked!")})

ラムダ式のお作法として、そのラムダ式が唯一の引数である場合はブラケットの外に出せる。
そして、ブラケットを省略できる。
さらに、ラムダ式に渡る引数の型(view:View)が明らかな場合に省略できるから以下のようになる


btn.setOnClickListener() { view: View -> Log.v("Hello Kotlin","clicked!")}
btn.setOnClickListener { view: View -> Log.v("Hello Kotlin","clicked!")}
btn.setOnClickListener { Log.v("Hello Kotlin","clicked!")}

コンパニオンオブジェクト

kotlinにはstaticメンバーは存在しない、のだけれども、
インスタンス化されていないクラス内部のコンパニオンオブジェクトのメソッドを呼び出す、
という仕組みによって、staticメンバー相当の呼び出しができる。

下記、Personクラスのプライマリコンストラクタはprivateであって外部からインスタンス化できない。
PersonクラスはFactoryオブジェクトを内包しており、Person.getInstance()というアクセスにより、
Factory.getInstance()が呼ばれ、Personインスタンスを返す。


class Person private constructor(var name: String)
{
    companion object Factory {
        fun getInstance(): Person {
            return Person("パーソン")
        }
    }
    override fun toString(): String {
        return "My name is ${this.name}"
    }
}
fun main(args: Array) {
    val p1 = Person.getInstance()
    println(p1)
}

Enumクラス

列挙型。class予約語の前にenumで置いてclassを修飾する。
列挙子は識別子で型はない。
列挙子の型チェックはis演算子で行う。


enum class Season {
    SPRING,SUMMER,AUTUMN,WINTER
}
fun main(args: Array) {
    val a = Season.SPRING
    println(a) // SPRING
    println(Season.SPRING == Season.SUMMER) // false
    println(Season.SPRING is Season) // true
    println(Season.SPRING is Enum) // true
}

列挙型にプロパティを持たせられる。書き方が特徴的だけれども以下の通り。
列挙子の()はSeasonのプライマリコンストラクタで、
順序数(ordinal)の他に、新たに定義したvalueをもたせている。
列挙型のプライマリコンストラクタはprivateであって、外から明示的にインスタンス化できず、
列挙する時にのみ実行される。


enum class Season(val value: Int) {
    SPRING(1),SUMMER(2),AUTUMN(4),WINTER(8)
}
fun main(args: Array) {
    val a = Season.SUMMER
    println(a) // SUMMER
    println(a.ordinal) // 1
    println(a.value) // 2
}

さらにEnumクラスにメソッドを定義できる。
列挙子の列挙と、Enumクラスのメソッド定義の間がセミコロン「;」になっている。


enum class Season {
    SPRING {
        override fun getDisplayName(): String = "春"
    },
    SUMMER {
        override fun getDisplayName(): String = "夏"
    },
    AUTUMN {
        override fun getDisplayName(): String = "秋"
    },
    WINTER {
        override fun getDisplayName(): String = "冬"
    };
    abstract fun getDisplayName(): String

}
fun main(args: Array) {
    for (season in Season.values()) {
        println(season.getDisplayName())
    }
}