default eye-catch image.

kotlin基本構文-パッケージ,継承,final修飾子,abstract,多態性,ダウンキャストとSmart cast,インターフェース

参考にしている書籍が薄すぎて悲しくなるが、サクサクっと読み進めてみる。 やはりJavaだからかクラス関係の記述が多いな。 パッケージのインポート kotlinのインポート構文はJava相当の書き方ができる。 パッケージfoo.barに含まれるgooクラスをインポートする書き方と、 foo.barに含まれる全てのクラスをインポートする(static import)書き方は以下。 import foo.bar.goo import for.bar.* kotlinはパッケージのトップレベルにクラスだけでなく関数を置けるので、 import構文も関数を指定することができる。 foo.barパッケージのトップレベルにあるgetValue()をインポートする書き方は以下。 import foo.bar.getValue 別パッケージの同一クラスをimportする際には、 Javaでは完全修飾して書かないといけないが、kotlinではas予約語を使って別名でインポートすれば良い。 import foo.bar.getValue as barGetValue import foo.hoge.getValue as hogeGetValue fun hoge() { println(barGetValue(10)) println(hogeGetValue(20)) } 継承 kotlinのクラスはデフォルトでfinal。デフォルトで継承できない。 open修飾子をつけることでfinalが外れ、継承できるようになる。 オブジェクト指向モデリングを少しかじると、やたら継承関係を作りたくなる参考書が多くて、 継承関係でないのにやたら継承関係にする人が多いと聞いたな...。 継承はextendsではなく「:」。 派生クラスのプライマリコンストラクタで基底クラスのプライマリコンストラクタを呼んでいる。 関数(kotlinはクラスのメンバ関数をメソッドと呼ばないで関数と呼ぶらしい)のデフォルトもfinal。 open class Person(private var name: String = \"Tom\") { open public fun sayHello() { println(\"My name is ${name}\") } } class LuckyPerson(private var name: String = \"Tom\", private var happiness: Int = 100) : Person(name) { override public fun sayHello() { println(\"My name is ${name}, my happiness is ${happiness}\") } } var person = LuckyPerson() person.sayHello() // My name is Tom, my happiness is 100 派生クラスがプライマリコンストラクタを持たない場合、 基底クラスのコンストラクタを呼ぶ書き方が別にある。 super()で基底クラスのコンストラクタを呼び出す。 open class Person(private var name: String = \"Tom\") { open public fun sayHello() { println(\"My name is ${name}\") } } class LuckyPerson : Person { var name: String var happiness: Int constructor (name: String, happiness: Int) :super() { this.name = name this.happiness = happiness } override public fun sayHello() { println(\"My name is ${this.name}, my happiness is ${happiness}\") } } var person = LuckyPerson(\"Tom\",200) person.sayHello() メンバ関数のデフォルトがfinalということは、 openなメンバ関数をoverrideした後、finalに戻す責任があるということ。 派生は悪、という宣言。。 open class Person(private var name: String = \"Tom\") { open public fun sayHello() { println(\"My name is ${name}\") } } class LuckyPerson(private var name: String = \"Tom\", private var happiness: Int = 100) : Person(name) { final override public fun sayHello() { println(\"My name is ${name}, my happiness is ${happiness}\") } } var person = LuckyPerson() person.sayHello() // My name is Tom, my happiness is 100 抽象クラス、抽象メソッド 抽象クラス、抽象メソッドはabstract予約語を使う。 指定位置は予想の通りで特別な印象はない。 abstract class Person(private var name: String = \"Tom\") { abstract public fun sayHello() } class LuckyPerson(private var name: String = \"Tom\", private var happiness: Int = 100) : Person(name) { final override public fun sayHello() { println(\"My name is ${name}, my happiness is ${happiness}\") } } var person = LuckyPerson() person.sayHello() // My name is Tom, my happiness is 100 多態性と異種リスト 多態性と異種リストのサンプル。何も難しいことはない。 abstract class Animal() { abstract fun sayHello() } class Dog() : Animal() { final override fun sayHello() { println(\"bow\") } } class Cat() : Animal() { final override fun sayHello() { println(\"mew\") } } var animals:Array = arrayOf(Dog(),Cat()) animals.forEach { animal -> animal.sayHello() } // bow mew ダウンキャストとSmart Cast 派生クラスの固有メンバ関数を呼び出すとき、インスタンスのis a関係をチェックしてから 派生クラスにダウンキャストして呼ぶのが普通だと思っていたけども、 kotlinは、is a関係のチェックとダウンキャストを自動的に行ってくれる。 以下の通り、animalインスタンスのis aチェックを行ったあと、 animal.how()および、animal.groom()のコンテキストでは、 animalがそれぞれDog,Catのダウンキャストとなっている。 open class Animal() { } class Dog() : Animal() { fun how() { println(\"howling\") } } class Cat() : Animal() { fun groom() { println(\"grooming\") } } var animals:Array = arrayOf(Dog(),Cat()) animals.forEach { animal -> if (animal is Dog) { animal.how() } // Smart cast to Dog else if (animal is Cat) { animal.groom() } // Smart cast to Cat } インターフェース kotlinは多重継承をサポートしていない。 振る舞いを複数に分割しておいて必要な振る舞いを実装するモデル。 振る舞いはインターフェース(純粋仮想クラス)として書くようになっている。 インターフェースはabstract関数(ex. breath())だけでなくデフォルト実装(ex.sleep())を持てる。 当然、クラスは複数のinterfaceを実装できる。 interface Biological { fun breath() fun sleep() { println(\"goo\") } } interface Mammalian { fun nursing() } fun main(args: Array) { open class Animal() : Biological, Mammalian { final override fun breath() { println(\"foo.\") } final override fun nursing() { println(\"nursing.\") } } var animal = Animal() animal.breath() animal.sleep() animal.nursing() } インターフェースの名前解決 kotlinではインターフェースがデフォルト実装を持てるので、 実装クラス側から定義済みのデフォルト実装を呼ぶケースがある。 もし、実装する複数のインターフェースが同じ名称を持つメンバ関数を持っていた場合、 競合する複数のメンバ関数から どれを呼ぶのか解決する必要がある。 実装クラスからインターフェースを参照する場合には、super予約語を使う。 interface Biological { fun breath() { println(\"foo\") } } interface Mammalian { fun breath() { println(\"boo\") } } fun main(args: Array) { open class Animal() : Biological, Mammalian { override final fun breath() { super.breath() super.breath() } } var animal = Animal() animal.breath() }

default eye-catch image.

kotlin基本構文-クラス,プロパティ,アクセス修飾子,コンストラクタ

言語仕様に拘りがあるタイプではなさそうで、かなり平易。 クラスの定義 public,private修飾子を指定したメソッドを持つクラスを定義してみる。 インスタンス化はnewが要らないんだな。 class Person() { public fun sayHello() { println(\"hello,kotlin\") sayYes() } private fun sayYes() { println(\"yes\") } } val person = Person() person.sayHello() // hello,kotlin yes 気になるのは、アクセス修飾子が無いときの振る舞い。 デフォルトのアクセス修飾子はprivateではなくpublic。 Javaのデフォルトのアクセス修飾子は\"package内のみ\"だった。 class Person() { fun hogehoge() { println(\"hogehoge\") } } val person = Person() person.hogehoge() メンバ変数とプロパティ Delphiで便利だったプロパティ。getter,setterを文法で解決する。 まずメンバ変数は以下の通り普通に定義できる。 これは後述するgetter,setterを省略した書き方。 class Person2() { var name = \"Ann\" fun show() { println(\"My name is ${name}.\") } } val person2 = Person2() person2.show() ageというメンバ変数に対してsetterを定義する。 外部からageというメンバ変数に値を代入する際に定義したsetterが呼ばれる。 異常値の入力値チェックが出来たりする。 getter,setterを文法で解決したメンバ変数をプロパティと言ってるらしい。 getter,setterでメンバ変数を参照する際、selfを使わずfieldという予約語を使う様子。 backing fieldという名前が付いてる。 class Person2() { var name = \"Ann\" var age = 10 set (value) { if (value < 0) { println("指定した値は不正です。") } else { field = value } } fun show() { println("My name is ${name}. My age is ${age}") } } val person2 = Person2() person2.show() // My name is Ann. My age is 10 person2.age = 20 person2.show() // My name is Ann. My age is 20 person2.age = -10 // 指定した値は不正です。 person2.show() // My name is Ann. My age is 20 なお、プロパティを変数(var)ではなく定数(val)で定義すると、読み取り専用プロパティになる。 class Person3() { val age = 20 } var person3 = Person3() person3.age = 100 // Error:(39, 5) Kotlin: Val cannot be reassigned プロパティの初期値を設定せず、コンストラクタでも代入しなかった場合はインスタンス化時にエラーとなる。 abstractクラスならOK。 class Person4() { val age public fun say() { println(\"${age}\") } } var person4 = Person4() person4.say() //Error:(42, 9) Kotlin: Property must be initialized or be abstract プロパティのsetterにアクセス修飾子を指定できる。 public修飾子がついたプロパティのsetterにprivate修飾子をつけると、 クラス内部からはsetter経由で値を変更できるが、外部からは変更できなくなる。 class Person5() { var name = \"Ann\" var age = 10 private set (value) { if (value < 0) { println("指定した値は不正です。") } else { field = value } } fun show() { println("My name is ${name}. My age is ${age}") } } val person5 = Person5() person5.show() // My name is Ann. My age is 10 person5.age = 20 // Error:(34, 5) Kotlin: Cannot assign to 'age': the setter is private in 'Person5' プライマリコンストラクタ コンストラクタはinit予約語の後に書く。コンストラクタのパラメタはinitに書かずclassに書く。 コンストラクタのパラメタがclassに付いているのでinit以外でも使ってしまいそうになるがinitでしか使えない。 コンストラクタに渡した文字列を使ってメンバ変数を初期化してみる。 class Person(name: String) { var name: String init { this.name = name println(\"${this.name} initialized\") } public fun sayHello() { println(\"say hello\") } } val person = Person(\"Tom\") person.sayHello() この書き方は、constructor予約語が省略されたものであって、 constructor予約語を省略しない場合は以下のようになる。 class Person constructor(name: String) { var name: String init { this.name = name println(\"${this.name} initialized\") } public fun sayHello() { println(\"say hello\") } } val person = Person(\"Tom\") person.sayHello() コンストラクタの実行とメンバ変数の初期化を一度に書くのは以下。 class Person(var name: String) { init { println(\"${this.name} initialized\") } public fun sayHello() { println(\"say hello\") } } val person = Person(\"Tom\") person.sayHello() アクセス修飾子までコンストラクタに含められる。 class Person(private var name: String) { init { println(\"${this.name} initialized\") } public fun sayHello() { println(\"say hello\") } } val person = Person(\"Tom\") person.sayHello() さらにデフォルト値を含められる。 class Person(private var name: String = \"Tom\") { init { println(\"${this.name} initialized\") } public fun sayHello() { println(\"say hello\") } } val person = Person() person.sayHello() セカンダリコンストラクタ 2つ以上のコンストラクタを書くときのやり方。 2つ目以降は順当にconstructor予約語を書いていく。 ほとんどの場合コンストラクタは1個だから1つ目のコンストラクタを簡易な方法でかけるようになっているのかな。 :thisというのは、プライマリコンストラクタを呼び出す書式。 ブロックの先頭に書くのではなく専用の書式がある。 セカンダリコンストラクタはプライマリコンストラクタを必ず呼ばなければならない。 class Person(var name: String = \"Tom\") { init { println(\"My name is ${name}\") } constructor(): this(\"Alice\") {} public fun sayHello() { println(\"say hello\") } } val person = Person() person.sayHello()

default eye-catch image.

kotlin基本構文-関数,引数のデフォルト値,引数の名前付き呼び出し,可変長引数,複数の戻り値,高階関数,ラムダ式

参考にした入門書の内容が薄すぎてトレースする意味がなくなってきた。 いきなり中級書で良かった気がする。 関数 kotlinにおける関数の定義方法。 これもDelphiっぽいけどDelphiの特殊な部分を普通に寄せた感じ。 今思えばDelphiの\"関数内の最終評価値が関数の戻り値になる\"動きはRubyに通じてるような。 関数の呼び出しに使ったスタックをどう解釈するかの違いだから似たパターンになるんだろう。 # kotlin fun getSquare(value: Int): Int { return value * value } println(getSquare(10)) // 100 # Delphi function getSquare(value: Integer): Integer begin result:= value * value end writeln(getSquare(10)); // 100 関数の定義において、引数の型を省略することはできない。 以下はN.G. fun getSquare(value): Int { return value * value } 戻り値が無い関数を定義する際は、戻り値の型をUnit型にする。 なお、関数の戻り値のUnit型のみ省略可能。 fun sayHello(msg: String): Unit { println(msg) } fun sayHello(msg: String) { println(msg) } よくある省略形として、関数本体が1行しかない場合はブラケットを省略できるけど、 kotlinも省略できる。ただし書き方が少し違う。 コンパイラがワンライナーの型を推測できる場合は、関数の戻り値の型自体を省略できる。 // 戻り値の型推論なし fun getSquare(value: Int): Int = value * value // 戻り値の型推論あり fun getSquare(value: Int) = value * value 引数のデフォルト値 よくある引数のデフォルト値定義。普通。 fun getSquare(value: Int = 10): Int { return value * value } println(getSquare()) // 100 デフォルト値ありの引数と無しの引数を混在させることもできる。 必須引数は引数リストの先頭に、省略可能引数は引数リストの後方に置くこと。 fun getMult(value1: Int, value2: Int = 10): Int { return value1 * value2 } println(getSqure(100)) // 1000 引数のデフォルト値にはリテラルだけでなく関数の戻り値を指定することができる。 これは新感覚。 fun getDefaultValue(): Int { return 15 } fun getMult(value1: Int, value2: Int = getDefaultValue()): Int { return value1 * value2 } println(getMult(100)) //1500 引数の名前付き呼び出し 関数の呼び出し時に引数の名前を指定することができる。 ほんと、どっかで見た最近の言語のつまみ食い。 引数の順序に縛られない、引数の順序によらずどの引数も省略できる。 fun getMult(value1: Int, value2: Int = 10): Int { return value1 * value2 } println(getMult(value1=100, value2=200)) // 20000 kotlinはJavaのコードを呼び出せる(はず)だけれども、 Javaのバイトコードは引数の名前を保持していないため、 Javaのメソッドを名前付き引数を使って呼び出すことはできない。 可変長引数 だいたいどの言語にもある可変長引数。 使ったこと無いけど。 他の変数と混在させる場合は、可変長引数を末尾に配置すること。 可変長引数を前に持ってくる場合、呼び出し時にどこまでが可変長引数かわからなくなるので、 可変長引数の後の引数を名前付きで渡す必要がある。(そこまでして順番に拘る必要はないと...) fun getValues(vararg values: Int): Int { var result = 1 for (value in values) { result *= value } return result } println(getValues(1,2,3,4,5)) // 120 可変長引数として配列を渡すことができる。 その際、配列の先頭にスプレット演算子(*)を置く。 ポインタ、参照とは関係なく、 単に配列を可変長引数に展開することから\"spread\"という名前になっている。 fun getValues(vararg values: Int): Int { var result = 1 for (value in values) { result *= value } return result } var ary: intArrayOf(1,2,3,4,5) println(getValues(*ary)) //120 スプレッド演算子を使うと、リテラルと配列から1つの可変長引数を作れる。 こんなことしないと思うんだけど。 fun getValues(vararg values: Int): Int { var result = 1 for (value in values) { result *= value } return result } var ary: intArrayOf(1,2,3,4,5) println(getValues(10,*ary,10)) //12000 複数の戻り値 これは鮮やか。関数が複数の戻り値を返せる。 鮮やかだけども型として常用してはいけない様子。関数の戻り値を受け取る時ぐらいで留めるべき。 以下は、可変長引数で渡した値の算術平均を返す関数。 fun getAvarange(vararg values:Int): Pair { var result = 0 var count = 0.0 for (value in values) { result += value count++ } return Pair(result, result/count) } println(getAvarange(1,2,3,4,5)) // (15,3.0) Pairをそれぞれの変数にバラすのは以下の通り。 val pairs:Pair = Pair(10,5.0) val (intValue, doubleValue) = pairs println(intValue) // 10 println(doubleValue) // 5.0 高階関数 関数の参照を渡すには以下のようにする。 Int型の引数を出力する関数printを、配列のforEachに渡している。 配列のforEachの繰り返しの度にprint関数が評価される。 その際、引数itemとして配列の各要素が渡される。 fun print(item: Int) { println(item) } var ary = arrayOf(1,2,3,4) ary.forEach(::ary) // 1 2 3 4 もちろん、関数をラムダ式に置き換えることができる。 ブラケットで囲まれた部分がラムダ式。 arrow(->)の左辺がラムダ式のパラメタ、右辺が関数の本体。 var ary = arrayOf(1,2,3,4) ary.forEach({ num:Int->println(num) }) ラムダ式のパラメタの型はコンパイラの型推論で自動的にわかるのなら省略できる。 var ary = arrayOf(1,2,3,4) ary.forEach({ num->println(num) }) ラムダ式を与える引数リストにおいて、ラムダ式が最後である場合、 引数リストからラムダ式を除いて以下のようにかける。 var ary = arrayOf(1,2,3,4) ary.forEach(){ num->println(num) } さらに、ラムダ式を与える引数リストにおいて、引数がラムダ式しかない場合、 引数リストの空プラケットさえ省略できる。 var ary = arrayOf(1,2,3,4) ary.forEach { num->println(num) } そして、ラムダ式に与える引数が一つしかない場合、暗黙の引数itを宣言無しで使える。 var ary = arrayOf(1,2,3,4) ary.forEach { println(it) } ラムダ式と匿名関数 あまりラムダ式と匿名関数の違いについて考えたことがなかったのだけども、 ラムダ式の代わりに匿名関数を渡すことができる。同じ意味になる。 ラムダ式は匿名関数の糖衣構文(Syntax sugar)である。 つまり冗長な匿名関数を簡易な書き方で書き直したのがラムダ式である。 下記の通り、匿名関数は冗長。 var ary = arrayOf(1,2,3,4) ary.forEach (fun(num: Int):Unit { println(num) }) ラムダ式内でのreturn Rubyで一つの話題になるラムダ式内でのreturnについて。 kotolinにおいてラムダ式内でreturnすると、該当のラムダ式ではなく直上の関数を抜ける意味になる。 下記はラムダ式を中断するだけでなくary.forEachを包含する関数も中断する。 従って、末尾のprintln(\"OK\")は処理されず、OKは出力されない。 var ary = arrayOf(1,2,3,4) ary.forEach { if (it>3) return println(it) // 1 2 3 } println(\"OK\") ラムダ式のみを中断するにはラベル構文を使用する。 下記のようにloopラベルを書いておくと、ラムダが中断したとき中断の連鎖はloopラベルで終了する。 これは自棄っぱちな感じがするな。Rubyと同等で良いんじゃなかろうか。 var ary = arrayOf(1,2,3,4) ary.forEach loop@ { if (it>3) return println(it) // 1 2 3 } println(\"OK\") // OK 高階関数サンプル 高階関数のサンプル。ある時間がかかる処理を関数として実装し、 高階関数にその関数を渡す。 高階関数側では、その関数の実行前後の時間を計測し経過時間を出力する。 fun calc(action: ()->Unit): Long { var start = System.currentTimeMillis() action() var end = System.currentTimeMillis() return end - start } val time = calc { var x = 1 for (i in 1..10_000_000) { x++ } } println(\"処理時間:${time}\") ローカル関数 関数内で関数を定義することでスコープを限定できる。 内側の関数からは外側の関数のスコープにある変数を参照できる。 fun outerFunction() { val constant = 100 fun innerFunction() { var variable = constant * constant println(variable) } innerFunction() } outerFunction() / 10000

default eye-catch image.

kotlin 基本構文 – 型,リテラル,null許容型,型変換,配列,コレクション,定数

アウトプットによる記憶の定着の意味で超入門書をトレースして書き起こす。 超入門が終わったら中級書に着手。 変数と型宣言 変数は宣言して使う。構文がまんまDelphiみたい。 kotlinは行末セミコロン不要。 こんなところで主張しなくても良いのにと思う。 # kotlin var name: String = \"default value\" println(name) // default value # Delphi var name: String = \"default value\"; writeln(name); // default value 型は以下の通り。Javaみたいに全てはクラスです!とか言わない。 プリミティブはプリミティブ。 データ型概要 Boolean真偽 Byte8bit整数 Short16bit整数 Int32bit整数 Long64bit整数 Float32bit浮動小数点 Double64bit浮動小数点 Char文字 String文字列 静的型付けなので定義した型以外の値を代入できない。 下記はIntelliJ IDEA上でコンパイル前にエラーであることがわかる。 var num: Int num = 100 num = \"hoge\" // Error:(5, 11) Kotlin: Type mismatch: inferred type is String but Int was expected 型推論の挙動は以下の通り。最初に代入した値の型が変数の型となり、 2回目以降の代入で異なる型を代入できない。 型推論のミスマッチはコンパイルしたときにわかる。 var num = 108 num = 100 num = \"hoge\" // Error:(3, 9) Kotlin: This variable must either have a type annotation or be initialized 変数宣言時に初期値を省略することができるが、型と初期値を両方省略することはできない。 これは変数宣言時に型推論の情報を決めるという仕組みでそうなっている。 var num num = 100 // Error:(3, 9) Kotlin: This variable must either have a type annotation or be initialized どんな値も入る魔法の型が存在する。Any型 var name: Any name = 100 name = \"hoge\" 数値リテラル 10進数、16進数、2進数、指数。普通。他の言語と同じ。 var name: Int name = 105 name = 0xFF name = 0b11 name = 1.234e+5 Rubyにもあったけども、桁表現ができる。 絶対使わないだろうけど、知ってたら知ってたで無用に使ってみたくなる。 var name: Int name = 105_200_300 println(name) // 105200300 型サフィックス 数値リテラルのデフォルト型はInt、浮動小数点数リテラルのデフォルト型はDouble。 リテラルに型サフィックスを付けることで、型を変更できる。 型サフィックスはLong変数のL、Float型のFのみ。 絶対使わなそうなパターンが用意されていないところの割り切りが、妥当な感じがする。 var intval = 300 println(intval::class) // class kotlin.Int var intval2 = 300L println(intval2:class) // class kotlin.Double var fval fval = 3.14 println(fval::class) // class kotlin.Double fval2 = 3.14F println(fval2::class) // class kotlin.Float 文字列リテラル 文字列リテラルはダブルクォート1つで括るタイプと、ダブルクォート3つで括るタイプの2つ。 前者はPHP,Rubyの流れではなく、あくまでJava。後者はPHPのヒアドキュメント相当。 var msg = \"\"\" コトリンコトリン。コトリンコトリン。 複数行にわたって文字列をかける。 コトリンコトリン。コトリンコトリン。 \"\"\" println(msg) // コトリンコトリン。コトリンコトリン。 // 複数行にわたって文字列をかける。 // コトリンコトリン。コトリンコトリン。 PHPでいつも気持ちが悪い、ヒアドキュメント内のインデントの空白問題。 バー(|)を書き.trimMargin()を呼ぶことで、コード上のインデントを有効として残したまま変数内のインデントを無視できる。 var msg = \"\"\" |コトリンコトリン。コトリンコトリン。 |複数行にわたって文字列をかける。 |コトリンコトリン。コトリンコトリン。 \"\"\".trimMargin() println(msg) //コトリンコトリン。コトリンコトリン。 //複数行にわたって文字列をかける。 //コトリンコトリン。コトリンコトリン。 変数展開はPHPと同じように$を使う。 var value = 100 var msg2 = \"This is value ${value}\" var msg3 = \"This is value $value\" println(msg2) // This is value 100 println(msg3) // This is value 100 PHPの場合、変数の後に文字列を続けても変数分を自動認識してくれたが、kotlinはしてくれない。 変数の後には空白を書くか${}で囲む必要がある。 var value = 100 var msg4 = \"This is value $valuehoge\" // Error:(13, 32) Kotlin: Unresolved reference: valuehoges null許容型 kotlinはデフォルトでnullを不許可。 変数宣言のときに型の末尾に\"?\"を付けることでnullを許容できるようにできる。 var str:String = \"hogehoge\" str = null // Error:(18, 11) Kotlin: Null can not be a value of a non-null type String var str2:String? = \"hogehoge\" str2 = null println(str2) // null null許容型の変数をnull不許可の変数に代入することはできない。 代入される側をAny型にしたとしてもN.G.。 var str:String = \"hogehoge1\" var str2:String? = \"fugafuga\" str = str2 // Error:(19, 11) Kotlin: Type mismatch: inferred type is String? but String was expected var stra:Any = str2 // Error:(19, 20) Kotlin: Type mismatch: inferred type is String? but Any was expected ぬるぽ防止構文 (Safe access operator) Nullableな変数がnullだった場合は、その変数にアクセスするとぬるぽが起こる。 ぬるぽを徹底して排除するために、メンバにアクセスする際にセーフコール演算子を使う。 変数がnullであった場合、セーフコール演算子はnullを返し、nullでない場合に限りメンバを返す。 var str: String = \"hogehoge\" println(str.length) // 8 var str: String? = null println(str.length) // Error:(19, 16) Kotlin: Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String? var str2: String? = null println(str2?.length) // null 普通にnullable型とnullを比較することが出来るので、if文でぬるぽ回避することもできる。 var str:String? = null if (str != null) println(str.length) 左辺がnullであった場合に限り指定した右辺を返す演算子(?:)も積極的に使える。 var str:String? = null println(str?.length ?:0) // 0 implicitな型変換 kotlinは基本的に暗黙の型変換が効かない。 Javaでは拡大方向の暗黙的型変換が許容されているがkotlinでは不可。 以下、Int型の変数をLomg型の変数に代入しようとしているがN.G.。 var x:Int = 100 var y:Long = x // Error:(4, 18) Kotlin: Type mismatch: inferred type is Int but Long was expected 変数宣言時の初期化の際にも暗黙の型変換が効かないので、 型サフィックスを付けて、変数の型と初期値を一致させる必要がある。 var m:Float = 1.5 // Error:(3, 19) Kotlin: The floating-point literal does not conform to the expected type Float var m:Float = 1.5f println(m) // 1.5 基本的に、というのは、整数リテラルについては暗黙の型変換が行われる。割り切り。 var i:Long = 105 var j:Short = 40 var k:Byte = 10 explicitな型変換 基本的にimplicitな型変換に頼らないで、explicitに型変換する方が良さそう。 var i = 10 var j:Long = i.toLong() 配列型 配列型はC++のジェネリックみたいに書く。 getter/setter,iterator,accessorが準備されていて使いやすい。 nullableも指定できる。 var ary:Array = arrayOf(\"1\",\"2\",\"3\") var ary2:Array = arrayOf(\"1\",\"2\",null) intArrayOf()はArrayを返すので、型推論のヒントとして与えることができる。 var ary3 = intArrayOf(1,2,3) Ruby風に、配列生成の初期値としてラムダ式を与えることができる。 Arrayの第1引数は配列のサイズ、第2引数はラムダ式。 第2引数のラムダ式の中に現れる変数iはラムダに渡される変数であってiでなくても何でも良い。 ラムダと高階関数は後で出てくるのでそこで。 var data = Array(5, {i -> i*3 }) data.forEach{ println(it) } // 0 3 6 9 12 配列のアクセサは[...]演算子を使う。 範囲外アクセスは実行時例外。 var data = Array(5, {i -> i*3 }) println(data[0]) // 0 println(data[1]) // 3 println(data[10]) // Exception in thread \"main\" java.lang.ArrayIndexOutOfBoundsException: 10 コレクション arrayの他に、list,set,mapなどがある。 var list:List = listOf(\"hoge\",\"fuga\") println(list) // [hoge, fuga] var list2 = listOf(\"hoge\",\"fuga\") println(list2) // [hoge, fuga] var set:Set = setOf(\"A\",\"B\",\"C\") println(set) // [A, B, C] var set2 = setOf(\"A\",\"B\",\"C\") println(set2) // [A, B, C] var map = mapOf(\"first\" to 1, \"second\" to 2, \"third\" to 3) println(map) // {first=1, second=2, third=3} 定数 kotlinの定数はval予約語により定義する。 定数への再代入は不可。コンパイル前にIntteliJ IDEAでエラーがわかる。 val constant = \"THIS IS CONSTANT VALUE\" println(val) // THIS IS CONSTANT VALUE val = \"RE SET VALUE\" // Error:(18, 5) Kotlin: Unresolved reference: constanct 要はfinalなので、変数自体は変更できないが、変数の中身は変更できる。 例えば、配列を定数として定義したとき、配列自体を変更できないが、中身を変更できる。 val ary:array = arrayOf(1,2,3) ary = arrayOf(10,20,30) // Error:(19, 5) Kotlin: Val cannot be reassigned ary[0] = 100 println(ary[0]) // 10

default eye-catch image.

kotlin hello world anyway.

XCodeが10になって色々言われていてビビってあまり本質的でないところに引っかかりそうだったのと、 そもそもXCode10が全然落ちてこないので、先にkotlin@IntelliJ IDEA@Macを試した。 しばらくkotlinの言語仕様の勉強をしてみることにする(Androidアプリにしないで)。 kotlin overview JVM上で実行される静的型付けのオブジェクト志向言語。 本家の解説を1行ずつ噛み締めながら読んでいく。 引用文に意訳を付けていくセルフ輪講形式。 Kotlin is a great fit for developing Android applications, bringing all of the advantages of a modern language to the Android platform without introducing any new restrictions: kotlinはAndroidアプリケーション開発に適した言語であって、モダンな言語が持つ利点を導入によって新しい制約を持ち出すことなしにAndroidプラットフォームにもたらす。 Compatibility: Kotlin is fully compatible with JDK 6, ensuring that Kotlin applications can run on older Android devices with no issues. The Kotlin tooling is fully supported in Android Studio and compatible with the Android build system. 互換性。kotlinはJDK6と完全な互換性がある。kotlinアプリケーションが古いAndroidデバイスで動作することを保証する。 Performance: A Kotlin application runs as fast as an equivalent Java one, thanks to very similar bytecode structure. With Kotlin\'s support for inline functions, code using lambdas often runs even faster than the same code written in Java. パフォーマンス。kotlinアプリケーションはJavaアプリケーションと非常に似たバイトコード構造となることで、Javaアプリケーションと同等の速度で動作する。 Interoperability: Kotlin is 100% interoperable with Java, allowing to use all existing Android libraries in a Kotlin application. This includes annotation processing, so databinding and Dagger work too. 相互互換性。kotlinはJavaと100%相互互換性がある。従って、全ての既存のAndroidライブラリはkotlinアプリケーションで動作する。この振る舞いには追加処理が含まれ、データバインディングやデバッガもまた動作する。 Footprint: Kotlin has a very compact runtime library, which can be further reduced through the use of ProGuard. In a real application, the Kotlin runtime adds only a few hundred methods and less than 100K to the size of the .apk file. フットプリント。kotlinは非常に小さな実行時ライブラリである。ProGuard使用時によりも小さくなる。また、kotlinランタイムは、たかだか2,300の新しいメソッドが加わるだけであり、.apkのサイズは100KB未満となる。 Compilation Time: Kotlin supports efficient incremental compilation, so while there\'s some additional overhead for clean builds, incremental builds are usually as fast or faster than with Java. コンパイル時間。kotlinは効率的なインクリメンタルコンパイルをサポートしている。従って、(ベースとなる)Javaのクリーンビルドと比較してオーバーヘッドがあるにも関わらず、インクリメンタルコンパイラによるコンパイル時間はJavaのそれと比べて同じかより速い。 Learning Curve: For a Java developer, getting started with Kotlin is very easy. The automated Java to Kotlin converter included in the Kotlin plugin helps with the first steps. Kotlin Koans offer a guide through the key features of the language with a series of interactive exercises. 学習曲線。Java開発者にとって、kotlinを始めるのは非常に簡単である。まずは、kotlinプラグインに含まれるJava->kotlin自動コンバータの恩恵を受ける。 anyway 参考にしたのは以下。 正味1時間くらいで環境構築できた。 Kotlin入門 IntelliJ IDEAで開発環境の構築とHello World 他にはAndroid Studioがないといけないんだろう...。 kotlinの文法をチェックするだけなら、 kotlin playgroundで出来る。 とりあえずhello,world.出してみる。 Scalaみたく.classが出力される。 コンパイル速度は遅くはないけど瞬殺って訳でもない。 2013LateのMBPでhello worldが2秒くらい。 10月29日にkotlin1.3がリリースされてる。 .classを経由しないでいきなりNativeになるとか。 今のところ聞かなかったことに...。 kotlinの名前の由来はコトリン島。 サンクトペテルブルグに近いバルト海に浮かぶ島だそう。 JetBrains社のサンクトペテルブルグにある研究所が作った言語なんだそうで。 とりあえず、とりあえず、でわからないままAndroidアプリにしても、 何も意味がないので、kotlinの言語仕様から。 結構続く。