default eye-catch image.

Kotlinスタートブックを読み始める

断片的な情報を集めるよりも、評判が良い良書を読む方が確実だし結果的に理解が早いと思う。 なので、超入門に続いて、Kotlin中級書として最も評判が良いだろう本を読み始めてみる。 何事もアウトプットしないと理解が定着しないので、引き続きWPへ転記。 超入門に使った書籍が薄すぎて...と思っていたのだけど、 もしかすると、さっそくKotlinの簡潔さに触れたのかもしれない。 言語仕様からくだらない拘りを捨てて、いかに現実的な書き方を実現するか、というところ。 これは良いかもしれないぞ、と。 Kotlinスタートブックposted with amazlet at 18.12.10リックテレコム (2017-03-21)売り上げランキング: 68,877Amazon.co.jpで詳細を見る 実際読んでみると、超入門で使ったKindle本で良かったかなと思う。 確かに上の方が書いてある幅が広いんだけど、下の方が要点がまとまっている気がする。 こちらの方が薄いし500円で買えるし。 速習 Kotlin: Javaより簡単!新Android開発言語を今すぐマスター 速習シリーズposted with amazlet at 18.12.10WINGSプロジェクト (2018-09-26)売り上げランキング: 1,981Amazon.co.jpで詳細を見る Amazon Cyber MondayでKindle Paper whiteを手に入れたので、 この手の本は、今後はKindleで入手する見込み。

default eye-catch image.

Swift超入門_関数とクロージャ的な何か

仮引数と省略 swift初心者(おっさん)が一見して理解できなかったのが関数の仮引数の前にあるアンダースコア(_)。 swift全般的にアンダースコアが言語仕様に含まれているので、いろいろ調べてみた。 単に、関数を呼び出す際にキーワード引数の名称を省略するための記法だった。 仮引数の前にアンダースコア(_)を置くと、呼び出し時にキーワード無しで引数を呼べる。 func hoge(_ int: Int) { print(int) } hoge(100) func hoge2(int: Int) { print(int) } hoge2(int: 200) Swiftは、仮引数と実引数を違う名前で定義できる。 以下、param2というキーワードを使ってhoge3を呼び出しているが、 hoge3の中では仮引数param2をparam3として使っている。 一般に、param3をparam1と関係がある名称にすることで冗長にさせない工夫。 func hoge3(param1: Int, param2 param3: Int) { print(\"(param1),(param3)\") } hoge3(param1: 100, param2: 200) デフォルト引数は一般的。 func hoge4(param1: Int, param2 param3: Int = 200) { print(\"(param1),(param3)\") } hoge4(param1: 100) 関数の定義名に特徴がある。hoge4の定義名は以下のようになる。 ObjectiveCっぽい。 hoge4(param1:param2) インアウト引数 いわゆる参照渡し。 仮引数にinout予約語をつけておくと、仮引数の操作が実引数に反映される。 呼び出す際に、引数に&をつける。 func hoge5(param1: Int, param2: inout Int) { param2 = 300 print(param1, param2) } var val1 = 100 var val2 = 200 hoge5(param1: val1, param2: &val2) // 100 200 print(val2) // 300 可変長引数 可変長引数はスリードット(...)を指定し、配列(Array)で受ける。 func hoge6(params: String...) { for param in params { print (\"element is (param)\") } } hoge6(params: \"hoge1\",\"hoge2\",\"hoge3\") クロージャ さて、Swiftのクロージャ。まぁ何回か使ったら慣れるだろうぐらい分かりづらくはない。 前置詞のinがクロージャブロックの開始になってるのは意図があるんだろうか。 let double = { (x:Int) -> Int in return x * 2 } let v = double(2) クロージャの引数の型と戻り値の型は型推論が効く。 var closure: (Int) -> Int closure = { (x:Int)-> Int in return x*x } closuer(2) // 4 var closure2 = { x in return x*2 } closure2(3) // 6 クロージャの簡易引数名 クロージャの引数名すら書きなくない場合もある。 前置詞inではなく、一般的なブラケットでクロージャブロックを書く。 クロージャブロックからは、n番目のパラメタを$nとして受け取る。 let add: (Int,Int)->Int = { return $0+$1 } add(2,3) // 5 クロージャによる変数束縛 クロージャの実行時にクロージャ呼び出し元の環境を利用できる。 文脈によって呼び出し元の環境は異なるので、実行時に環境のスナップショットを取るイメージ。 Swiftでは\"キャプチャ\"という名前で呼んでいるらしい。 例えば以下。クロージャ add:(Int,Int)-> Int 内で呼び出し元の変数pを使っている。 クロージャ呼び出し時に呼び出し元の環境に依存してpの値が決まる。 なお、pが初期化済みでない状態でクロージャを定義するとエラーになる。 クロージャの実行前にキャプチャする変数は初期化されている必要があるらしい。 var p : Int = 200 let add: (Int,Int)->Int = { return $0+$1 + p } p = 200 add(2,3) // 205 p = 300 add(2,3) // 305 クロージャ内でキャプチャした変数を書き換えた場合、キャプチャ元になった変数自体が変わる。 autoclosure属性 引数の遅延評価を行う指令。 論理和を計算する関数を定義しようとしたとき、Bool型の引数を2つ取ってBool型の戻り値を返す 関数を定義するとする。 論理和なので、どちらかが真であれば一方の引数を見なくても結果がわかるのだが、 下記のようにすると、どちらかが真であっても、もう一方の引数を評価して返すことになる。 func or(_ lhs:Bool, _ rhs:Bool) -> Bool { if lhs { return true } else { return rhs } } or(true,false) 引数をクロージャとし、クロージャ内で引数の評価の必要性を判断するようにして、 クロージャを遅延評価(関数内での評価)することで不要な引数の評価を回避するというアイデア。 func or2(_ lhs:Bool, _ rhs:()->Bool) -> Bool { if lhs { return true } else { let rhs = rhs() return rhs } } func lhs() -> Bool { return true } func rhs() -> Bool { return false } or2(lhs(),{return rhs()}) or2の第2引数が複雑になってしまうところを、文法解決で簡単に書けるようにするのが@autoclosure。 引数をクロージャで包む書き方を省略できる。 下記のように書ける。 func or3(_ lhs:Bool, _ rhs: @autoclosure ()->Bool) -> Bool { if lhs { return true } else { let rhs = rhs() return rhs } } func lhs() -> Bool { return true } func rhs() -> Bool { return false } or3(lhs(),rhs()) trailing closure 引数の最後がクロージャである場合、その引数の前で引数のブラケットを閉じて、 その後にクロージャを書くことができる。 例えば以下のような感じ。 1つ目は真面目に引数の最後にクロージャを与えている。 2つ目は関数呼び出しの後にクロージャを書いている。 func execute(parameter:Int, closure: (String)->Void) { closure(\"parameter is (parameter)\") } execute(parameter:1, closure:{ string in print(string) }) execute(parameter:2) { string in print(string) } 関数をクロージャとして扱う 関数をクロージャとして扱うことができる。関数double(_:)は(Int)->Int型。 それをfunctionという定数の初期値に設定している。 functionには型アノテーションがないけれども、 double(_:)が(Int)->Intなので型推論されて(Int)->Intになる。 func double(_ x:Int)->Int { return x*2 } let function = double function(2) 関数の引数としてクロージャを与えるシーンで、 クロージャを関数として用意しておくことで、それぞれの呼び出しでクロージャを定義する必要がなくなる。 下記について、上はmapの引数としてそれぞれクロージャを定義して呼び出している。 同じ処理をするのに2回定義しないといけない。 下は、関数を定義し、mapの引数として関数をクロージャとして与えている。 関数(クロージャ)の定義は1回で良い。 let array1 = [1,2,3] let toArray1b = array1.map{$0*2} let array2 = [4,5,6] let toArray2b = array2.map{$0*2} func double(_ x:Int)->Int { return x*2 } let toArray1a = array1.map(double) let toArray2a = array2.map(double)

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