Kotlin M13で追加されたlateinit試してみた
Kotlin マイルストーン13 がリリースされました!
その中で追加されたlateinit
というプロパティにつける修飾子が便利そう。
プロパティの初期化を先延ばしにできるので既存のフレームワークにインジェクトしてもらえるって寸法です。
「プロパティの初期化の先延ばし」っていうとDelegated propertyを思い出すけど、これには問題があったのでした。。
lateinitってDelegates.nonNull()でも同じことできるんじゃないの
— たろう (@ngsw_taro) 2015, 9月 16
@ngsw_taro バイトコード見ると、「private final Lkotlin/properties/ReadWriteProperty; _hello$delegate」ってなってるので、型がちがくてインジェクションしてもらえなくて(´・ω・`)
— しおしお (@_siosio_) December 3, 2014
ということでlateinit
を試してみました。
import com.google.inject.Guice import javax.inject.Inject import kotlin.properties.Delegates class Greeter { fun greet() { println("Hello") } } class Client { @Inject lateinit val greeter1: Greeter @Inject val greeter2: Greeter by Delegates.notNull() } fun main(args: Array<String>) { val injector = Guice.createInjector() val client = injector.getInstance(Client::class.java) client.greeter1.greet() //=> Hello client.greeter2.greet() // IllegalStateException thrown }
Google Guiceでインジェクトしています。
lateinit
が付いているgreeter1
は見事インジェクトされています!しかし、Delegates.notNull()
をつけているgreeter2
はインジェクトされておらずgreet()
呼び出し時に初期化が済んでないよと例外が投げられてしまいました。
そのほかの変更点についてはまた後ほど。
ちなみにこれならいける
var greeter2: Greeter by Delegates.notNull() @Inject set
第3回かわいいKotlin勉強会を開催しました #jkug
開催しました!!!
第3回です!!
44名の方にご来場いただきました。 申し込み自体はキャンセル混みで86名です。 すごい人気です。
会場をご提供してくださいましたドリコム様(@sue445さん)、 運営スタッフの@yy_yankさんにこの場をお借りして感謝申し上げます。
発表資料
Kotlinらしいコード (私)
Server Side Kotlin (@yy_yankさん)
個人開発AndroidアプリをKotlinにガチ移行してみた話 (@kiriminさん)
Kotlin as an AltJS (@mike_neckさん)
(あやぴーさん)
普段C#使っている僕から見た、Kotlin (@RyotaMurohoshiさん)
Kotlin M12 言語仕様変更のまとめ
ついにKotlinマイルストーン12がリリースされました! 公式ブログはこちら。 言語仕様の変更についてまとめます。 今回はわりと多いです。
@ngsw_taro 変更多くて修正が大変かと思われますが、Analyze -> Code cleanupでプロジェクト内のKotlinソースファイルすべてに対してdeprecatedなコードを新しい構文に変換してくれますよ
— たろう (@ngsw_taro) May 30, 2015
アノテーション
アノテーションの記法がJavaっぽくなりました。
つまりM12より前では[Foo(args)]
と書いていたものを、M12以降では@Foo(args)
と書くことになります。
今まで通り@
を付けずに済むこともあります。
ラベル
このアノテーションの変更に伴い、ラベルの書き方も変わります。
今までは@name
だったものをname@
と@
を最後に持ってくる必要があります。
loop@ for (i in 1..100) {
for (j in 1..100) {
if (...)
break@loop
}
}
アノテーションでKClassが使えるようになった
アノテーションの引数として受け取るクラス参照が、M12より前だとjava.lang.Class
限定でした。
そのため引数を渡す側のコードはjavaClass<MyClass>()
のようなちょっとイケてない記述になっていました。
M12からはjava.lang.Class
の代わりにkotlin.refrect.KClass
が使えるようになります。
プライマリコンストラクタ
今までプライマリコンストラクタはclass User(val id: Long)
のように記述していました。
M12でもこの記法は有効ですが、full formで記述するとclass User constructor(val id: Long)
になります。
このfull formは、プライマリコンストラクタに対してアノテーションや修飾子を付けるときに使用することになります。
インタフェース
trait
キーワードは非推奨となり、interface
キーワードに取って代わられます。
列挙型
Javaのenumと非常に近くなりました。 つまり、エントリ間はカンマで区切り、メンバを持たせる場合には最後のエントリにセミコロンを置き、エントリの直後にコンストラクタへのパラメータリストを記述できるようになりました。詳細はこちら。
関数型
通常の関数型が要求される場面で、拡張関数を渡せるようになったらしいです。メソッド参照がJavaのそれと同じような感じになったということです。具体的には(String) -> Int
が要求される箇所でString::length
のようなString.() -> Int
型の関数参照を渡せます。
// M12より前はこう書いてた strs.map { it.length() } // M12からはこう書ける (M12より前は型エラーになってた) strs.map(String::length)
KotlinでJavaみたいなメソッド参照をする - 算譜王におれはなる!!!!で面白い(けど複雑な)アプローチを考えていたのでこの改善は嬉しいです!
スマートキャスト
スマートキャスト(nullチェック含む)がより賢くなりました。
var
な変数に対してもコンパイラが追える範囲で、スマートキャストが有効になります。
名前付きリターン
高階関数の引数として直接記述する関数式内での名前付きリターンがサポートされたようです(M11でもされてたような?)。
関数式はfun(arg)=...
のように書く関数リテラルみたいなやつのことです。
削除された機能と非推奨になった機能
削除
class object
。代わりにcompanion object
を使いましょう。- 匿名イニシャライザ。
init
を付けたブロックを使いましょう。
非推奨
when
でのbreak
とcontinue
- インタフェースのクラス継承
- スーパタイプの共変特殊化
- 静的型の表明
(ネタ)Kotlinで暗黙の引数いろいろ
Kotlinの関数リテラルはその引数が唯一つだけの場合、暗黙の引数としてit
が使用できるのはみなさんご存知だと思います(暗黙の引数という言葉は正確じゃないような気がするけど気にしない)(知らない人は公式ドキュメントか今日発売のSoftwareDesignを見るといいよ)。
listOf(1, 2, 3, 4, 5) .filter { n -> 2 < n } // 引数を明示することも .map { it * 2 } // itを使うこともできる。 .let { println(it) } // => [6, 8, 10]
Scalaっぽく
Scalaの関数リテラルの暗黙の引数は、複数の引数に対応しています。それぞれ_
で参照することになりますが、参照するたびに第一引数、第二引数のように変化します。
// Scala Seq(1, 2, 3).reduce(_ + _)
こういう感じの暗黙の引数がKotlinにも欲しかったので作ってみました。
// Scalaっぽい暗黙の引数_が使える(Int, Int)->Intな関数のためのクラス class ScalaFunc2(val a: Int, val b: Int) { var count: Int = 0 val _: Int get() = when(count++) { 0 -> a 1 -> b else -> throw IllegalStateException() } }
そして独自のreduce
を定義。
fun List<Int>.myReduce(f: ScalaFunc2.()->Int): Int = reduce { a, b -> ScalaFunc2(a, b).f()}
f
はScalaFunc2
の拡張メソッドです。
そして実際に使ってみる!
listOf(1, 2, 3).myReduce{_ + _}
成功です!
しかし残念なことにScalaFunc2<A, B, C>
のように一般化できません。
ScalaFunc2
の_
プロパティが参照されるたびにその型を変化させることができないからです。
Clojureっぽく
Clojureの無名関数における暗黙の引数も複数の引数に対応しています。
第一引数は%1
、第二引数は%2
という具合で参照できるので引数の呼び出し順序とか回数とかを気にせずに使用できます。
; Clojure (reduce #(+ %1 %2) [1, 2, 3]) ; わざわざ無名関数定義せず+だけでいいんだけどいい例が思いつかず...
これをKotlinで。
// Clojureっぽい暗黙の引数が使える(A, B)->Cな関数のためのクラス class ClojureFunc2<A, B>(val _1: A, val _2: B) fun <A> List<A>.myReduce(f: ClojureFunc2<A, A>.()->A): A = reduce { a, b -> ClojureFunc2<A, A>(a, b).f() }
今回は型を一般化できました。
listOf(1, 2, 3).myReduce{_1 + _2}
以上です。