算譜王におれはなる!!!!

偏りはあると思うけど情報技術全般についてマイペースに書くよ。

Schooに出演してKotlin語って来たよ〜!詳しい回答つき

f:id:ngsw_taro:20170608110405j:plain

昨日、オンライン動画学習サービスのSchooさんの生放送授業に出演しました。

schoo.jp

Android開発言語にKotlinが正式に仲間入りというニュースがきっかけで、初めてKotlinを知ったという開発者も多くいらっしゃると思います。 そのような人たちのために、サクっとKotlin紹介みたいなノリで1時間お話しさせていただきました。

小さい勉強会でのLTから大きなカンファレンスでの講演まで数多く経験してきて、人前でしゃべることには緊張こそすれ慣れているつもりでした。 しかし「生放送」というものは初めての経験だったので、勝手がわからず緊張しているのかリラックスしているのか、よくわからないテンションで臨みました。

普通の勉強会は、聴衆の反応を直接感じることができ、臨機応変に振る舞えるものだと思います。 要は軌道修正やそのタイミングの察知が容易なのかなぁと。

一方、生放送という形は、リアルアイムで視聴者のコメントが届き、ある程度の反応はわかるものの、「楽しんでもらえてるかな?」とか「難しすぎないかな?」という不安を話しながら感じていました。 とはいえ、ネットを介すことでコメントや質問がしやすいようにも思えます。 リアル勉強会では最後の質問コーナーで、挙手して皆の前で質問する人は決して多くないのではないかと思います(多くても質問の数は3個とか4個)。 昨日の生放送では、20個くらいは質問をいただけたと思います。

特に主張したいことはないのですが、初めての形態での情報発信をできたことを純粋に楽しめたし、私自身 非常に勉強になりました!

いただいた質問を詳しく回答

いただいた質問に対して丁寧に回答したつもりでしたが、プログラミング言語のことなので、やはり言葉だけでは伝わりづらい部分もあるのだと思います。 なので、コードを添えて回答したいと思ったものを、この場で解説します。

Q. データクラスのプロパティの妥当性検証はできるのか?

できます。 Kotlinのクラスには「イニシャライザ」というものがあり、ここでインスタンス生成に関する事前条件の検証等を行います。

例えば人物を表すクラスPersonを考えます。次のような実装になるものとします。

data class Person(val name: String, val age: Int)

このような実装ですと、次のように年齢に負数をセットすることができてしまいます。

val taro = Person("Taro", -1)

このような不正な状態のインスタンスを生成したくありません。 そこで「イニシャライザ」の登場です。 クラスPersonを、次のように書き直します。

data class Person(val name: String, val age: Int) {
    init {
        if (age < 0)
            throw IllegalArgumentException("ダメ")
    }
}

この新しいPersonインスタンスを生成するときにPerson("Taro", -1)とコンストラクタを呼び出すと、例外がスローされます。 不正な状態のインスタンスを生成することを防げた、というわけです。

ちなみに、このような事前条件の検証を行うための便利な関数が標準ライブラリに含まれています。 それを使って書き直すと、こうなります。

data class Person(val name: String, val age: Int) {
    init {
        require(age >= 0, {"ダメ"})
    }
}

Q. データ隠蔽はできるか?

できます。 Kotlinのプロパティへのアクセスは一見、内部データに直接アクセスしているように見えますが、違います。 内部データは「バッキングフィールド」という形で保持しますが、外部からこれを直接触ることはできません。 すなわち内部データと、それへのアクセスを提供する窓口は別に提供されます。 デフォルトでデータ隠蔽できているのです。

class Foo {
  val foo = "Foo"
}
Foo().foo //=> Foo

プロパティfooへのアクセスは、内部データを直接見ているような感じがします(Javaに慣れている人なら特に)。 しかし、実はそうではないことを、次のコードで確認することができます。

class Foo {
    val foo: String = "Foo"
    get() {
        println("fooを返します")
        return field
    }
}
Foo().foo
// fooを返します
// Foo

fieldは暗黙の変数で、カスタムゲッターやカスタムセッターの中でのみアクセス可能です。 このfieldが内部データの正体です。外の世界からこいつに接触することはできません。

ひとこと

「髪短くしたね!」って言われるのを期待しているのに「黒くなったね!」とよく言われます。

GoogleがKotlinをAndroid開発言語として公式サポートするってよ!

blog.jetbrains.com

苦節5年、Kotlinエバンジェリストを自称して啓蒙活動に励んできましたが、ついに! KotlinがAndroidアプリの開発用言語としてGoogleのお墨付きをもらいました! おめでとう!

毎年毎年、Google I/Oのキーノートを見ながら友達と「今年はKotlin来てほしいなー」なんて話していましたが、まさか今年来るとはね。

KotlinでAndroidを始める方は拙著がおすすめです!

Kotlinスタートブック

Kotlinスタートブック

また、日本Kotlinユーザグループでは勉強会や交流会を開催していますので、興味ある方はぜひ!

kotlin.connpass.com

#DroidKaigi でKotlinハンズオンの講師をしたよ〜!

f:id:ngsw_taro:20170315105718j:plain:w600

3/9(木)、3/10(金)にDroidKaigiに行ってきました。 2日目にはKotlinハンズオンの講師を務めました。 KotlinとAndroidを絡めたハンズオンをやるのは実は初めてで、結果的にはおおむね成功でしたが、課題がいくつか残りました。

90分という枠にいろいろ詰めすぎたことを一番反省しています。 RetrofitとDagger、async/awaitはスコープに含めるべきではありませんでした。 これらを盛り込んだ理由は、RetrofitやDaggerなど AndroidJavaで便利に使えるライブラリをKotlinからも難なく使えるんだよ ってことを示したかったからです。 async/awaitは、DroidKaigiより1〜2週間前にKotlin 1.1がリリースされたこともあり「せっかくだから紹介しよう」というノリで入れてしまいました。

ご協力いただいたチューターの方々のブログもご覧ください。

サンプルプロジェクトはGithubで公開しています(https://github.com/ntaro/github-client-for-droidkaigi)。 スライドはこちら↓

次回ハンズオンの宣伝

日本Kotlinユーザグループ x teratail でお送りするKotlinハンズオンが4/15(土)に開催されます! 3/22(水)から募集開始です。 レベル感としては「Kolintは初めてだけど他の言語でプロダクト作ってるよ」って人を対象としています。 ぜひお越しください!

kotlin.connpass.com

Kotlin 便利なプラグインが2つ登場しRealmが捗るよ〜

blog.jetbrains.com

Kotiln 1.0.6がリリースされました! 同時に面白いプラグインが2つリリースされました。 all-open compiler pluginとno-arg compiler pluginです。

all-open compiler pluginは、指定したアノテーションがついたクラスが自動でopen指定されるという機能を持ちます。 no-arg compiler pluginは、指定したアノテーションがついたクラスに、自動でデフォルトコンストラクタ(引数を持たないコンストラクタ)を生成してくれます。

導入方法は、冒頭に示したKotlin公式ブログを参照してください。

今回はこれらのプラグインを用いることで、Realmが使いやすくなることを示したいと思います。

プラグインなし

まずは普通にKotlinだけを使って、Realmオブジェクトを定義してみます。

open User(@PrimaryKey open var id: Long = 0,
          open var name: String = ""): RealmObject

うひゃー、おまじないだらけですね。

openが3回も登場していますが、RealmがこのUserクラスを継承して面白い機能を追加するために必要なのです。

また、Realmはデフォルトコンストラクタを要求します。 そのためUserクラスのプライマリコンストラクタでデフォルト引数を与えることで、引数なしのコンストラクタを提供しているというわけです。

ともかく、本来であれば無視してもよいことに注意しなければならないのですね〜。

all-open compiler pluginを使う

all-open compiler pluginを使って、open修飾子を退治しましょう。 プラグインの設定として、下記をbuild.gradleに記述します。

allOpen {
    annotation('io.realm.annotations.RealmClass')
}

この設定により、@RealmClassアノテーションが付いたクラスが、自動でopen指定となります。

@RealmClass
User(@PrimaryKey var id: Long = 0,
     var name: String = ""): RealmObject

no-arg compiler pluginを使う

次にno-arg compiler pluginを使って、デフォルト引数を毎回記述する退屈な作業から解放されましょう。 プラグインの設定として、下記をbuild.gradleに記述します。

noArg {
    annotation('io.realm.annotations.RealmClass')
}

all-openのときと同じように、@RealmClassが付いたクラスを対象に、デフォルトコンストラクタの自動生成を設定します。

ついにやりました! 謎のopenもデフォルト引数も、キレイに消えたコードが手に入りました!

@RealmClass
class User(@PrimaryKey var id: Long,
           var name: String) : RealmObject()

おまけ

no-argで自動生成したデフォルトコンストラクタは、Kotlinのリフレクション機能を使ってもアクセスすることはできないようです。 一方Javaのリフレクション機能を使うとアクセスできます。

@MyAnnotation // このアノテーションに対してno-arg設定
data class Hoge(val value: String)
Hoge::class.constructors.forEach(::println)
println("----------")
Hoge::class.java.constructors.forEach(::println)
println("----------")
println(Hoge::class.java.newInstance())

実行すると...

fun <init>(kotlin.String): Hoge
----------
public Hoge()
public Hoge(java.lang.String)
----------
Hoge(value=null)