PUROGU LADESU

ポエムがメインのブログです。

速習Kotlin


* 変数
var xxx

* クラスありきではないのでトップレベルに関数をかける
エントリーポイント
fun main(args: Array<String>) {
    val name: String = "サンタ"
    println("こんちは ${name}さん")
}

*
プリミティブ(基本型)という概念はない
Boolean Byte Short Int Long Floag Double Char -> JVMではプリミティブとなる
String

型指定しなくても、型推論によって型が決まる
型を指定しない場合は初期値は必須

* 数値リテラル
整数: Int 小数点:Doubleが規定
変える場合は接尾辞をつけるL: Long, F: Float

num::classで型を表示

* 文字列リテラル
EscapedString 普通の文字列
RawString ヒアドキュメント
"""で囲む
trimIndent()でインデントを削除できる
trimMargin()で|より前を削除できる

* 文字列テンプレート
 ${val}
前後が空白の場合{}は省略可能だが使わなくて良いかも

* null許容
型名に?をつける Int? String?
ボクシング 基本型->ラッパーオブジェクトへ変換すること
Int -> Int? へ代入すると参照先がかわる??

* 比較
値の比較 == JavaのEquals
参照先の比較 === Javaの==

* Any
どの型でもOK、nullはダメ。Any?はnull許容型のみOK

* スマートキャスト
nullチェックをすればnullでない型として扱える

st2?.length // nullの場合null。nullかどうかチェック。
st2?.length ?: 0 // nullの場合0
st2!!.length // nullの場合例外。強制的に非null型に変換

* キャスト
Javaのように大きい型への暗黙的変換を認めない
明示的にやる
Int -> Long は val.toLong()
Float -> Double は val.toDouble()

* 配列
arrayOf(1, 2, 3)
intArrayOf(1, 2, 3)
set() get() が使える
要素は変えられないのでadd()は使えない

空の配列は要素数を指定し、null許容型のみ
var data: Array<String?> = arrayOfNulls(5)
var data = arrayOfNulls<String>(3)

コンストラクタ(要素数とラムダ)
var arr4 = Array(5, { i -> i * 3})

* コレクション
toList()を使えば別の参照として渡せる

* 定数
変数は var
val Javaのfinalになる(再代入不可)変更不可ではない
まずは valを使うようにする

* const
コンパイル時定数、コンパイル時点で決まっている
トップレベルかobjectのメンバー
基本型orString
getterを持たない


* 3項演算子はないがifが式なのでそれが使える
val sw1 = if (n1 > 0) "good" else "bad"

* Range
1..10 型はIntRange
1 until 10 末尾を含まない
10 downTo 1 減少
1..10 step 2

* ビット演算子
Java -> k& | ^ ~
Kotlin -> and or xor inv

0B0110 or 0B0001
Integer.toBinaryString() 2進数表記に

* while do while は javaと同じ

* if
式なので値を返せる
式として使う場合はif elseを網羅している必要がある

* when
Javaのswitch
breakは不要、defaultでなくelse

val sw3 = when {
        n1 == 8 -> "good"
        n1 < 0 -> "bad"
        else -> "soso"
    }

range, in, !で否定, カンマで列挙も使える
val sw4 = when(point) {
    in 5..9 -> "A"
    2,3 -> "B"
    1 -> "C"
    else -> throw Exception("input out of range")
}

* for
ループ変数はval, varはつけない
for (li in lis) { print(li) }
is.forEach { print(it) }

ラベル構文でfor, whileに名前をつけてbreak,continueの対象を指定できる
outer@while
break@outer

X to X
X in X


## 関数
引数、戻り値の定義は必須
戻り値がないUnitは省略可能

* 単一式の場合
式を{} でなく = の後ろに書く
returnも不要、型も省略可能

* 名前付き引数
イコールで指定
getArea(height = 5)

* 可変長引数
Arrayとして扱える
普通の引数を混在するには、先頭にするか、そうでなければ名前付き引数にする
fun logprint(vararg items: String) {
    items.forEach {
        print(it)
    }
    println()
}

* スプレッド演算子
配列を引数として展開
アスタリスク一個つける *list

* 複数の値を返す
これは一時的なもの以外は使い回さず、データクラスなどを使うべき
Pair<String, Int>
Triple<String, Int, Int>

fun pairRerutn(): Pair<String, Int> {
    val message = "成功"
    val status = 200
    return Pair(message, status)
}

呼び元は分解宣言で受け取る(無視するものは_を置く)
val (message, status) = pairRerutn()

forEachなど引数として関数を受け取るもの

メソッドに関数を渡す
::change
fun change()

普通はラムダ式を渡す
{item: String -> println(item)}

型が明白な場合は省略可能
{item -> println(item)}

一番うしろの引数がラムダ式の場合、括弧の外に出せる
() {item -> println(item)}
引数がラムダ式のみの場合()も省略可能

ラムダの中でreturnするとforEachでなくその外側の関数を抜ける
ラムダのみを抜ける場合はラベル構文を使う
forEach @loop {}
return@loop

* 匿名関数
ラムダは戻り値の型を表現できないのでその場合
fun(num: Int): Int { num }

高階関数を作るには、引数に型を指定し、内部で実行する
fun benchmark(action: () -> Unit): Long {
    action()
}

* インライン関数
関数の受け渡しはオーバーヘッドがあるので、高階関数などは有効
モジュールのサイズは大きくなる



* staticは存在しない

* メンバー関数
Javaではメソッド

* メソッドアクセス修飾子
public 規定(Javaはパッケージ内が規定)
protected 同じクラスとサブクラス(Javaはパッケージ内も可能)
internal 同じモジュール(IntelliJIDEAモジュール、Gradleプロジェクトなどコンパイル単位)
private 同じクラス、インナークラスのprivateは参照不可(Javaは可能


* プロパティ
単なるフィールドでなく、getter setterをもつ
val, varが使用可能。
初期値を入れない場合は、コンストラクタでセットする
field:バッキングフィールド。自動でフィールドを用意してくれる

get() {
    return field
}
set(value) {
    field = value
}

get() = age >= 20

クラス外からは変更不可にする
private set


* プライマリコンストラクター
class Human constructor(name: String, age: Int)
    var name: String
    init {
        this.name = name
    }

class Human(var name: String, var age: Int) {}

initにプライマリコンストラクタの内容を書く
constructorは省略できる
var をつけることで引数がプロパティとなり、渡された値で初期化される

class Human {}

引数がない場合は()を省略できる

複数のコンストラクタを作る場合
最終的に必ずプライマリコンストラクタを呼び出す(別のセカンダリコンストラクタ経由でもOK)
本体が不要な場合は{}は省略可能

constructor(name: String): this(name, 0)

デフォルト値を指定できる
class Human3(val name: String = "None", val age: Int = 0) {

* パッケージ
Javaと違いフォルダ構造とパッケージは対応してなくて良い
トップレベル(パッケージ直下)
public internal
private ファイル内で有効(パッケージ内ではなく)

* インポート
規定のインポート 明示しなくてもインポートされる KotlinStandardLibraryとか
アスタリスクでパッケージ全部インポート
as XXX でエイリアス
staticインポート Javaのようにimport staticはない*を使うだけ。
クラス名なしでstaticメソッドを使うため。


* open
規定では継承不可。Javaのfinalの状態
クラス、関数にopenをつけると継承、オーバーライド可能となる
open class xxx -> class xxx : xxx
open fun xxx -> override fun xxx

class Child : Parent()
()をつけることでコンストラクタを実行できる
これでconstructor(): super() はいらない

final override fun show()
これ以上overrideさせない場合にfinalをつける
クラス自体がopenじゃないと意味ない

* abstract
抽象メソッドを含んだクラスを抽象クラスという
抽象メソッドにはabstractをつける。オーバーライドが必須になる。

* interface
publicのみ(デフォルト)
abstract open は不要

プロパティを持てる(Javaではpublic static finalのみ)
プロパティはoverrideするかコンストラクタで初期化する必要がある

デフォルト実装(オーバーライドしなくても使える)
super.xxx()で呼び出す
名前が競合したら指定 super<Hoge>.xxx()

バッキングフィールドは持てない(他のプロパティを使う)
get() = this.xxx

プロパティの初期値は設定できない。getterを使うとできる
get() = 20


* 型変換
アップキャスト済みで実態が派生クラスであれば
var worker: Worker = BusinessWorker("bb")

スマートキャスト
明示的にキャストせず型をチェックすれば、基底ー>派生として動く
if (worker is BusinessWorker)
        worker.work()

明示的にチェックする場合はasを使う
var business: BusinessWorker = worker as BusinessWorker


## データクラス

プライマリコンストラクタの引数が1個以上
コンストラクタのみなら{}はなくて良い
※コンストラクタで定義していないものは無視される

equals
すべてのプロパティが等しい場合オブジェクトも同一とみなす

componentN
分割代入ができるようになる。
内部的にxxx.component1, xxx.component2, xxx.component3のようにプロパティにアクセス

copy
コピー。引数で変更するプロパティを指定できる。

* オブジェクト宣言
staticクラスが作れる -> トップレベル関数にしてパッケージにまとめる方が良い?
ユーティリティクラス
コンストラクタは無い
継承は可能

object Something
あとは普通のクラスのようにする

* オブジェクト式
object: Something
再利用しないその場限りのクラスを定義する??

* SAM変換
SAMインターフェイス: 一つしか抽象メソッドを持たないインターフェイス
JavaのRunnable Comparator
これをラムダ式に置き換える??

* コンパニオンオブジェクト
クラス内部でオブジェクト宣言することで、インスタンス化せずに呼び出せるメンバーを作れる
(staticメンバー)

class Laptop private constructor(var name: String) {
    companion object Factory {
    companion object { 無名でも良い
        fun getInstance(): Laptop {
            return Laptop("サーフェス")
        }
    }

Laptop.getInstance()

* enum class
シンボルをカンマ区切りで列挙
is で型のチェック可能
name 文字列
ordinal インデックス
entries 列挙
valueOf() 文字列で探す

enum class Season2(val code: Int) {
    SPRING(1),
    SUMMER(2),
    AUTUMN(3),
    WINTER(4);

class MyGen<T>(var value: T) {
val gen2 = MyGen<String>("tomorrow")
val gen3 = MyGen(12.3) // 推測できる場合は型指定を省略できる

typealias で型名を定義できる

Tの後ろまたは、whereで渡せる型を指定した型を継承しているもののみに制限
<T:Hoge>
where T:Hoge, T:Fuga

先頭に<T>を置く
クラスから独立して使える

fun <T> tail(list: Array<T>): T

* in out
Java 配列の型に親子関係にあれば代入できる
Kotlin 代入できない

out その派生型を認める
in その上位型を認める


## その他
* 拡張関数
既存のクラスに関数を追加する
openでなくても良い
自分はthisでアクセスできる

operatorをつけて引数と戻り値をクラスにする
operator fun plus(c: Coordinate): Coordinat

* 入れ子のクラス
入れ子を持つクラスをアウタークラスという
そのクラス専用のクラスとかは中に入れてprivateにする

openの場合はこれで呼べる
MacClass.Iphone()

* インナークラス
innerをつける
アウタークラスのプロパティにアクセスするにはthis@MacClass.nameとする

* 委譲プロパティ
プロパティの後ろにbyで変更時に実行する内容を追加
Delegates.observable("") { xx }
Delegates.vetoable(0) { 判定 } // falseで更新キャンセル

* 遅延プロパティ
初回参照時の処理を書く
インスタンス生成時ではなく、初回参照時に動作し初回以降は同じ値を返す

* プロパティをmapで管理
class Book(private val map: Map<String, Any>){
    val title: String by map
    val price: Int by map
}

* 委譲プロパティの自作
ReadWriteProperty<Any, T> を実装

* 委譲クラス
インターフェイスの実装を別でやる
class XXX: インターフェイス by 実装クラス