default eye-catch image.

Swift4基本構文_制御文的な何か

オプショナルバインディング</h3 Optional型の存在の有無を冗長な書き方にしないためのif-let構文。 if letで指定したOptional型の変数がnilでなければ、構文の中でアンラップ済みの変数を使えるようにする。 ダウンキャストもif letで対応できる。 let oa = Optional(\"a\") if let a = oa { print(\"(a)\") } else { print(\"nil\") } let ob = Optional(\"b\") if let a = oa, let b = ob { print(\"(a),(b)\") } else { print(\"nil\") } let any : Any = 1 if let iany = any as? Int { print(\"(iany)\") } guard,guard-let ある関数のスコープ内で、guardで指定した条件が満たされることを保証する。 関数の先頭で引数に対してguardを定義することで引数の前提条件を書いたりする。 if-letのguard版(guard-let)もある。 func someFunc() { let value = 1 guard value >= 0 else { return } // ここまで来たならvalueが0以上であることを保証する print(\"0以上です。\") } func someFunc2() { let any : Any = 1 guard let iany = any as? Int else { return } print(\"(iany)\") } nil可能な引数を取る加算関数をif,guard両方で書くと以下。 guardで書いた方が見通しがよくなる。 func add(_optionalA:Int?, _optionalB:Int?) -> Int? { let a: Int let b: Int if let unwrappedA = _optionalA { a = unwrappedA } else { return nil } if let unwrappedB = _optionalB { b = unwrappedB } else { return nil } return a + b } func add2(_optionalA:Int?, _optionalB:Int?) -> Int? { guard let a = _optionalA else { return nil } guard let b = _optionalB else { return nil } return a + b } for-in,for-case for-inで要素の全てにアクセスする。for-caseで条件を満たす要素にアクセスする。 foreachで回す先頭で条件でスキップする奴はfor-caseで書ける。 let dict:Dictionary = [\"a\":1,\"b\":2] for (key,value) in dict { print(\"(key):(value)\") } let ary:Array = [1,2,3,4] for case 2...3 in ary { print(\"2以上3以下\") } fallthrough switch文の各caseの終わりでfallthroughを書いたときに初めて後ろのcaseが実行される。 fallthroughを書かないと条件にマッチしたcaseした実行されない。 なので、switch-caseの各caseの末尾にbreakを書く必要はない。 let a = 1 switch a { case 1: print(\"case1\") fallthrough case 2: print(\"case2\") default: print(\"other\") } 遅延実行 関数の評価時ではなく、関数のスコープから抜けたときに実行する文を記述する。 以下、関数評価時にはgは0だが、関数がスコープから抜けたときにdeferが評価されgが1になる。 var g = 0 func someFunc2() -> Int { defer { g += 1 } print(\"(g)\") return g } someFunc2() print(\"(g)\")

default eye-catch image.

Swift4基本構文_型的な何か

Kotlinに超入門してみたので続いてSwiftに超入門してみる。 超入門に使用する書籍は以下。 [改定新版]Swift実践入門-直感的な文法と安全性を兼ね備えた言語 WEB+DB PRESS plus Playgroundsを使って確認しながら各単元の何かを書いてみる。 変数と定数 変数はvar、定数はletで宣言する。Kotlinと同様に宣言時に型情報が決まる必要がある。 つまり、変数(定数)宣言時に型アノテーションを書くか、初期値の型から型推論させるか。 型推論が出来るので、右辺から型が決まる場合には左辺の型定義(型アノテーション)は省略可。 let i: Int = 123 let j = 123 print(type(of:i)) // Int print(type(of:j)) // Int nilは代入不可。使用時までに値を入れている必要あり。 let i: Int = nil // Nil cannot initialize specified type \'Int\' let j: Int j = nil // Nil cannot be assigned to type \'Int\' let v: Int print(v) // Constant \'v\' used before being initialized スコープ 外側の宣言は内側の宣言で使える。逆はできない。特別な予約語は不要。普通。 let globalint: Int = 100 func hoge() { lef localint: Int = 200 print(globalint) // 100 print(localint) // 200 } Bool型,リテラル Bool型のリテラルはtrue,false。 演算子は論理和、論理積、否定のみ。普通。 let a: Bool = true let b: Bool = false print (a && b )// false print (a || b )// true print (!a) // false Int型,リテラル IntはInt8,Int16,Int32,Int64。 32bitプラットフォームでIntはInt32、64bitだとInt64。普通。 Float型,Double型,リテラル 32bitがFloat、64bitがDouble。 Float,Doubleは無限(isInfinite)と非数値(isNaN)という状態をStaticプロパティとして持つ。 let a: Double = 10.0 / 0.0 a.isInfinite // true let b: Double = 0.0 / 0.0 b.isNaN // true 異型間の代入 Swiftでは型が異なる変数同士の代入を暗黙的にできない。イニシャライザをかます。 縮小方向の代入は端数処理が行われる。 let a: Int32 = 100 let b: Int64 = a // Cannot convert value of type \'Int32\' to specified type \'Int64\' let c: Int64 = Int64(a) let x: Float = 1.0 let y: Int = Int(x) // 1 異型間の比較 Swiftでは型が異なる変数同士の比較ができない。イニシャライザをかます。 結構、型の違いにシビアなのか...。 Int32とInt64は比較できるみたい。FloatとDoubleはできない。 let a: Float = 100.0 let b: Double = 100.0 let c = (a == b) // Binary operator \'==\' cannot be applied to operands of type \'Float\' and \'Double\' let d = (a == Float(b)) // true String型,リテラル 文字列リテラルはダブルクォーテーション。エスケープは一般的なやつが使える。 リテラル内での変数展開は()演算子。${}の方がいいのに...。 let x: Int = 100 let v: String = \"hoge(x)fuga\" // hoge100fuga kotlinでも見かけたスリーダブルクォート。これの発祥はPythonらしい。 面白いのがスリーダブルクォート内において変数自体のインデント分が無視されること。 kotlinのTrimよりも強烈。以下、sとrの出力は同じになる。 let s: String = \"\"\" Swift入門 Swift入門 Swift入門 \"\"\" print(s) let r: String = \"\"\" Swift入門 Swift入門 Swift入門 \"\"\" print(r) String.Index StringとCharacterの関係も一般的。String内のCharacterを指す方法が用意されている。 String.startIndex(=0),String.endIndex(=文字数)を使ってCharacterを指す例。 let a: String = \"abcde\" let b: Character = a[a.startIndex] // a let lastindex = a.index(a.endIndex,offsetBy: -1) let c: Character = a[lastindex] // e Int型とString型の変換 イニシャライザを通す。変換できない場合はnilが入る。 let i: Int = 100 let s: String = String(i) let s2: String = \"hoge\" let i2 = Int(s2) // nil 文字列の結合 文字列の結合は+演算子。append(_:)も使える。 let s1: String = \"hoge\" let s2: String = \"fuga\" let s3: String = s1 + s2 let s4: String = s1.append(s2) 配列,配列リテラル 配列はArray。SyntaxSugarとして[Element]という書き方もできる。 配列リテラルは[]。同一型リテラルの配列から型推論可能。 順序数で要素にアクセス可能。 append(_:)で追加、insert(_at:)で挿入、remote(at:)で削除。 let ary1: Array = [1,2,3,4,5] let ary2: [Int] = [1,2,3,4,5] let ary3 = [1,2,3,4,5] // Intの配列 let ary4 = [\"hoge\",\"fuga\"] // Stringの配列 let a = ary1[0] // 1 ary1.append(6) // [1,2,3,4,5,6] ary1.insert(10,at: 1) // [ 1,10,2,3,4,5,6] ary1.remove(at: 0) // [10,2,3,4,5,6] ary1.removeLast() // [10,2,3,4,5] ary1.removeAll() // [] Dictionary型,リテラル Key->Value辞書はDictionary、SyntaxSugerとして[Key:Value]という書き方もできる。 辞書リテラルは[\"key1\":\"value1\",\"key2\":\"value2\"]のように書く。 同一型の辞書から型推論可能。 let dic1 : Dictionary = [\"a\":1,\"b\":2] let dic2 : [String:Int] = [\"a\":1,\"b\":2] let dic3 = [\"a\":1, \"b\":2] Keyは一意でないといけないので、Keyとして使える型には制限がつく。 DictionaryはDictionary。Key型はHashableプロトコル(インターフェース?) に準拠したもの(Keyからハッシュ値を計算できる)のみ使える。 DictionaryへのアクセスはOptional(Value)を返す。つまり無いキーの値はnil。 let dic3 = [\"a\":1, \"b\":2] let a = dic3[\"a\"] // 1 let b = dic3[\"c\"] // nil 更新,追加は同じ。存在するkeyを指定して値を代入すれば更新、存在しないkeyなら追加。 削除はnilを設定する。 var dic4 = [\"a\":1,\"b\":2] dic4[\"b\"] = 3 // [\"a:1\",\"b\":3] dic4[\"c\"] = 4 // [\"a:1\",\"b\":3,\"c\":4] dic4[\"a\"] = nil // [\"b\":3,\"c\":4] 範囲型 Swiftの範囲型は数学的なモデリングになってる。 1つ目は開区間、半開区間、閉区間。2つ目は順序(計数可能)。 数値以外の範囲についてもforループを回して順番に要素にアクセスできたりする。 範囲型が範囲を定義すると、範囲に収まる値が得られるのではなく、 区間と上限下限をもったオブジェクトのまま保持される。 let range1 = 1..<4 // CountableRange(1..<4) : lower bound=1,uppper bound=4 for value in range1 { print(value) // 1 2 3 } let range2 = 1...4 // CountableCloseRange(1..4) : lower bound=1,uppper bound=4 for value in range2 { print(value) // 1 2 3 4 } let range3 : Range = 1..<4 // Range<1..<4) :lower bound=1,upper bound=4 let range4 : Rnage = 1...4 // Range<1..4) : lower bound=1,upper bound=4 Optional型 型がnilを許容するか否か。Wrapped型がnilを許容する場合はOptional、許容しない場合はWrapped。 Suger Syntaxとして、OptionalをWrapped?と書くこともできる。 let ival1: Int = 32 let ival2: Int = nil // Nil cannot initialize specified type \'Int\' let ival3: Optional = 32 let ival4: Optional = nil let ival5: Int? = nil ぬるぽ防止(Unwrap) デフォルトではOptional型同士の四則演算はできない。 明示的なアンラップ(Optional->Wrapped)が必要。 ??演算子を使うと、左辺のOptional(Wrapped)がnilの場合に右辺、 nilでない場合にWrappedをアンラップして返す。 !演算子を使うと強制アンラップ。当然nilならぬるぽ。ぬるぽ時のエラーがドキッとする。 当然、強制アンラップは非推奨。 let ival6: Int? = 100 let ival7: Int? = 200 let ival8 = ival6 + ival7 let ival6: Int? = 100 let value1 = ival6 ?? 3 // 100 let ival7: Int? = nil let value2 = ival7 ?? 3 // 3 let ival8: Int? = 100 let ival9: Int? = 200 let ival10 = ival8! + ival9! // error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0). ぬるぽ防止(Optional chain) Optionalの値を使うために必ずアンラップしないといけないとなると面倒。 ?.演算子を使うと、nil、非nilの場合を1行に含めることができる。 左辺がnilである場合はnil、nilでない場合はOptional(Wrapped)を返す。 ?.を数珠つなぎに書いていくと、最初のnilで評価が止まってそれ以降のnil参照を行わない。(なのでchain) let optionalDouble1 = Optional(1.0) // Optional(1.0) let optionalInfinite1 = optionalDouble1?.isInfinite // Optional(false) let optionalDouble2 :Double? = nil let optionalInfinite2 = optionalDouble2?.isInfinite // nil 強制アンラップ構文 ぬるぽを恐れるあまり、nil可能なケースが面倒になっているのを和らげるためか、 何か迷いのようなものを感じるけれども、OptionalをWrapped?ではなくWrapped!と書くと、 型としてOptionalを維持したまま、暗黙的に強制アンラップしてから計算が行われる。 当然、nilが入っていればぬるぽで止まる。 let intval: Int? = 100 print(intval + 200) // Value of optional type \'Int?\' must be unwrapped to a value of type \'Int\' let intval: Int! = 100 print(intval + 200) // 300 let intval2: Int! = nil print(intval2 + 200) // error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0). アップキャスト 継承関係やプロトコル(インターフェース?)準拠により上位型の存在が確実な場合、 アップキャスト可能。アップキャストはas演算子。 確実な上位関係でない型へのアップキャストは実行時エラー。 アップキャストは暗黙的に実行可能。 let str : String = \"hogehoge\" let any : Any = str as Any let int : Int = str as Int // Cannot convert value of type \'String\' to type \'Int\' in coercion let any2 : Any = str ダウンキャスト 上位型から下位型へのキャスト。失敗のリスクがある。 失敗のリスクをOptionalで解決するのがas?演算子、リスクをケアしないのがas!演算子。 as?によるダウンキャストはOptionalを返す。as!はWrappedを返す。 as!が失敗した場合、実行時エラーが発生する。 let any = 1 as Any let int = any as? Int // Optional(1) let any2 = 1 as Any let int2 = any2 as! Int // 1

default eye-catch image.

Kotlin@IntelliJ IDEAでAndroid hello world.

古めのIntelliJ IDEAがAndroid Studioで使われている様子で、 IntelliJ IDEAを使おうがAndroid Studioを使おうが大差ないのかもだけども、 Androidの最新の動向をトレースできるのはきっとAndroid Studio。 以下の記事を順当に流してとりあえずKotlin Android Hello World環境ができた。 IntelliJ IDEAで、JavaSDK,AndroidSDK,Android support plugin,Gradle pluginの設定をするのは順当。 参考にしたやつ。 https://qiita.com/shitake/items/150d7a94b66ba73a93ba https://qiita.com/g0z4ru/items/24bca8e6fb691f29260f 今日現在、ext.kotlin_versionがデフォの1.3.0だといろいろ見つからないと言われるので1.2.71に設定。 // build.grade // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { ext.kotlin_version = \'1.2.71\' // change from 1.3.0 to 1.2.7 repositories { google() jcenter() } dependencies { classpath \'com.android.tools.build:gradle:3.1.0\' classpath \"org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version\" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } } MainActivity.kt。ちゃんとKotlinになってる。 5年前くらいにAndroidアプリを開発していたころと同じMacで作業しているのだけども、 エミュレータが遅いのは相変わらずな以外は、意外にもモッサリしていない。 当時はEclipse、今はIntelliJ IDEA(Android Studio)。 割と2013LateのMBPで何かやろうとする気になる。 サンプルをビルドするだけでなくて、 何か簡単なアプリを1つ作ってみよう..。

default eye-catch image.

kotlin基本構文-ジェネリッック型,ジェネリック関数,共変と反変,out予約語

ジェネリック プライマリコンストラクタで受け取った文字列をプロパティとして持つオブジェクトを作成し、 その文字列を取得するコードは以下の通り。普通。 class MyObject(var value: String) { fun getProp(): String { return this.value } } val m = MyObject(\"hogehoge\") println(m.getProp()) この書き方だと、型が文字列に限定される。 型を一般化して、文法で型と紐づける書式がジェネリック。 上記のコードをジェネリックで一般化してみる。 一般化した型引数をTとしている。 class MyGenerics(var value: T) { fun getProp(): T { return this.value } } val g1 = MyGenerics(\"hogehoge\") println(g1.getProp()) // hogehoge val g2 = MyGenerics(100) println(g2.getProp()) // 100 型推論による型指定の省略 プライマリコンストラクタに渡すパラメタの型からジェネリック型が唯一決まる場合は、 インスタンス化時の型指定を省略できる。 class MyGenerics(var value: T) { fun getProp(): T { return this.value } } val g1 = MyGenerics(\"hogehoge\") println(g1.getProp()) // hogehoge val g2 = MyGenerics(\"fugafuga\") println(g2.getProp()) // fugafuga 複数のジェネリック型指定 複数のジェネリック型を指定できる。下記のような書き方になる。 class MyGenerics2(var value1: T, var value2: R) { fun getProp1(): T { return this.value1 } fun getProp2(): R { return this.value2 } } val g3 = MyGenerics2(100,\"hoge\") println(g3.getProp1()) println(g3.getProp2()) 型引数の制約 型引数TをT:typeとすると、type型かtype型の派生型という意味になり、型の制約を作れる。 ジェネリック型をHogeクラスの派生に制限する定義として、インスタンス化時にInt型を指定する例。 コンパイル時(前)にちゃんとエラーが出る。 class Hoge(var def: String) { fun getVal(): String{ return \"hoge\" } } class MyGenerics3(var value: T) { fun getProp(): T { return this.value } } // Error:(44, 32) Kotlin: The integer literal does not conform to the expected type Hoge var g4 = MyGenerics3(100) println(g4.getProp()) val hoge = Hoge(\"hoge\") var g5 = MyGenerics3(hoge) println(g5.getProp().getVal()) ジェネリック関数 関数の引数と戻り値を関数の呼び出し時に決めて紐づけられる仕組み。 ジェネリック型の定義はclassだが、ジェネリック関数はclassではなく関数。 以下は、リストの末尾(tail)を応答するジェネリック関数。 引数として配列を取るが、配列の型を呼び出し時に決められる。 fun tail(list: Array): T = list[list.size-1] var data = arrayOf(1,2,3,4,5) println(tail(data)) 共変(covariant)と不変(invariant) Javaの話。Javaでは配列の要素のクラスに継承関係があるとき、 その配列は親クラスの配列に代入できる。 aaryの定義はNumberの配列だが中身はIntegerの配列になっている。 定義上Numberの配列だから、Numberの派生クラスのインスタンスを代入する式を書いてもコンパイルが通る。 例えば、Longのインスタンスを入れられる。 実態はIntegerの配列だから、Longのインスタンスを入れた瞬間に怒られる。 Integer[] iary = new Integer[10]; Number[] aary = iary; aary[0] = new Long(0); // Exception occured. kotolinの配列は共変ではなく不変。 配列の要素に継承関係があったとしても代入できない。 これを不変(invariant)と言ってkotolinは不変が基本。 var intArray: Array = arrayOf(1,2,3) var anyArray: Array = intArray // Exception occured. ただし、意図的に共変にすることもできる。 以下の通り、kotlinの配列は不変であるから ArrayをArrayに代入できない。 コンパイル時にひっかかる。 open class Hoge(var value:String) { open fun sayHello():Unit { println(\"hoge\") } } class Fuga(value:String): Hoge(value) { final override fun sayHello(): Unit { println(\"fuga\") } } var hogeArray: Array = arrayOf(Fuga(\"hoge\"),Fuga(\"fuga\")) //Error:(17, 34) Kotlin: Type mismatch: inferred type is Array but Array was expected var fugaArray: Array = hogeArray out予約語をつけることで、その配列を共変配列にできる! open class Hoge(var value:String) { open fun sayHello():Unit { println(\"hoge\") } } class Fuga(value:String): Hoge(value) { final override fun sayHello(): Unit { println(\"fuga\") } } var hogeArray: Array = arrayOf(Fuga(\"hoge\"),Fuga(\"fuga\")) var fugaArray: Array = hogeArray 実際、コレクションのListはoutが指定してある。 なので、それを知らずともJava的に共変配列の操作のようなことができる。 interface List: Collection var list: List = listOf(\"aaa\",\"bbb\",\"ccc\") var list2: List = list

default eye-catch image.

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()) } }

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の言語仕様から。 結構続く。