2017年11月13日月曜日

【書評】Kotlinイン・アクション

  • 公開日:2017年11月13日
  • 最終更新日:2017年11月29日
  • 学習期間:2017年11月06日 - 途中
  • 学習期間:705minites[途中]

初めに

本格的にkotlinを利用するようになってきたので、詳細な仕様を把握するために本書を購入しました。
この記事は感想とまとめを兼ねています。

本の評価には色々な視点がありますが、私はjavaのサーバーサイド、android, kotlinの開発経験があるので、その視点で本書を評価しています。

また、まとめの量が多くなってきたので、更新しながら感想を書いていきます。つまり、途中の殴り書きです。。。
良書だし、ちゃんと最後にまとめます。

紙 or 電子書籍

頻繁に持ち運ぶなら電子書籍。職場や家置いとくなら紙で良いと思います。私はkindleで買いました。kindleだと容量を70Mくらい使うので、空容量に注意しましょう。

本書の対象者

Javaの実装経験と、簡単なkotlinの実装経験がある人が読む本です。javaやkotlinの実装経験がない人が読む本ではありません。

kotlinの実装経験がない場合は、「Kotlinスタートブック」から読みましょう。

章別感想とまとめ

後々開発や復習で見返すようためのメモ書き。気になった事項の調査等。結構無駄に深く読み込んでいる箇所もあるので、そこは無視してください。

1章

kotlinは実用主義。アイデアを探求する言語ではない。
多重配列が使いにくいのはそれでかな?
もしくは、 Effective Javaの項目25「 配列よりリストを好む 」が適用されているか。。。

2章

if文(p23 - p24)

ifは式なので、値を返す

ブロック本体と式本体で記述可能

{project_folder}/inaction/If.kt

fun max (a : Int = 0, b : Int = 0) : Int {
  return if a > b : a ? b
}

{}で囲むことを「ブロック本体を持つ」という。
return文が必要。

式本体だけで書く

{project_folder}/inaction/If.kt

fun max (a : Int = 0, b : Int = 0) : Int = if a > b : a ? b

直接返す場合は「式本体を持つ」という。

式本体の関数の場合は、戻り値も省略可能

{project_folder}/inaction/If.kt

fun max (a : Int = 0, b : Int = 0) = if a > b : a ? b

変数

なるべくval(イミュータブル(不変))で宣言するべき。

クラス

valはイミュータブル(不変)なのでgetterのみ

varはミュータブル(可変)なのでgetterとsetterの両方作成

プロパティを呼び出すとき、実際はgetterを利用している。

greteな仕様だ。

カスタムアクセサ

使ったことない。p33

enum

私は、Android開発ではenumは使わないで、Support Annotationを使う派です。でも、Android Architecture Componentsでは普通に使われていますね。Lifecycleのpublic enum Stateとか。。。proguardで難読化すれば問題はないのですが。。。

Androidの神と呼ばれているJakeWhartonさんはenum使うべき派だそうです。参考
理由は、我々は組み込みエンジニアではなく、Webエンジニアである。との理由だそうです。AndroidエンジニアはWebエンジニアではなく、組み込みエンジニアであるじゃないのかな???よくわかりません。。。

when

whenは式なので値を返す。当然、ブロックを使うこともできます。
javaと異なり、オブジェクトも利用可能。本書では、setOfでSetオブジェクトを作成して比較している。

setOfはkotlinで以下のように実装されている。

{project_folder}/inaction/If.java

public fun <T> setOf(vararg elements: T): Set<T> = if (elements.size > 0) elements.toSet() else emptySet()

Setオブジェクトを返していますね。toSetはkotlin.collectionsパッケージで実装されています。

{project_folder}/inaction/If.java

/**
 * Returns a [Set] of all elements.
 *
 * The returned set preserves the element iteration order of the original array.
 */
public fun <T> Array<out T>.toSet(): Set<T> {
  return when (size) {
  0 -> emptySet()
  1 -> setOf(this[0])
  else -> toCollection(LinkedHashSet<T>(mapCapacity(size)))
  }
}

ここでもwhenを使ってますね。out Tのout 修飾子を利用しています。分散アノテーションともいいます。つまりクラスC は T の プロデューサ であり、 T の コンシューマ ではない、と考えます。

スマートキャスト

kotlinの地雷ですね、これ。ネットにもあまり良い説明が載ってない。どうしても好きになれない仕様。

isでチェックとキャストを両方やる。

valで宣言されている必要がある。

3章

トップレベル関数(p64 - 66)

説明がわかりにくいです。要は、javaのようにクラス宣言をしなくてもメソッドを作成できるし、呼び出せるってことですね。

まずはjavaで乗数を計算するクラスと実行クラスを実装してみます。

{project_folder}/inaction/Top.java

package inaction;

public class Top {
    public static Double topFunction() {
        double  a = 2.0;
        return Math.pow(a, 2.0);
    }
}

{project_folder}/Jikko.java

import inaction.Top;

public class Jikko {
    public static void main(String []args) {
        System.out.println(Top.topFunction());
    }
}

実行結果は[4]です。両方ともclassを宣言して、メソッドにstatic宣言をしています。
kotlinの場合は、classとstatic宣言が不要になります。

{project_folder}/Jikko.kt

import inaction.topFunction

fun main(args : Array) {
    System.out.println(topFunction())
}

{project_folder}/inaction/Top.java

package inaction

fun topFunction() : Int {
    val a = 2.0
    return Math.pow(a, 2.0).toInt()
}

結果は同じように4になります。javaクラスにコンパイルされる時に勝手にクラスが作成されて、メソッドがstaticになります。
さらに、ファイルクラスの名前も変更できます。

{project_folder}/inaction/Top.java

@file:JvmName("AnotherTop")
package inaction

fun topFunction() : Int {
    val a = 2.0
    return Math.pow(a, 2.0).toInt()
}


@file:JvmNameアノテーションでファイル名を変更できます。

呼び出してみます。

{project_folder}/inaction/Top.java

import inaction.AnotherTop;

public class Jikko {
    public static void main(String []args) {
        System.out.println(AnotherTop.topFunction());
    }
}

別名AnotherTopで呼び出せています。

拡張関数

これはkotlinを使っている人はみんな知っていますね。

拡張関数はオーバーライドは無理なことを覚えておきましょう。静的関数だから、当たり前ですね。

可変長引数

使うケースは、まずない気がしますが、忘備録的に記載しておきます。

{project_folder}/inaction/Kahen.kt

fun main(args : Array<String>) {
    accept("test1",  "test2", "test3")
}

fun accept(vararg msg: String) {
    System.out.println(listOf(*msg))
}

可変長引数は、スプレッド演算子「*」を使います。

to

mapOfで利用するtoは関数です。中置呼び出しと呼びます。toはPairクラスを返します。ScalaのTupleと同じ機能です。

{project_folder}/inaction/To.kt

    val map = mapOf(1 to "one", 2 to "two")
    for ((key, value) in map.iterator().withIndex()) {
        System.out.println("key:${key}, valu:${value}")
    }

key:0, valu:1=one
key:1, valu:2=two

トリプルクヴォート

あまり使い道がなさそう

ローカル関数

関数の中で関数の宣言をすることです。javaScriptのような記載が可能です。無駄な宣言を減らせます。

4章

kotlinのクラスやメソッドはデフォルトでfinal。継承可能なクラスはopenをつける必要がある。

{project_folder}/inaction/test.kt

class Button {
    fun disable() {}
    open fun amimate() {}
    fun click() {}
}

// finalエラーが出る。
class TextButton : Button() {

}

openを追加します。

{project_folder}/inaction/test.kt

open class Button {
    fun disable() {}
    open fun amimate() {}
    fun click() {}
}

class TextButton : Button() {
    override fun amimate() {
        super.amimate()
    }

    // これはfinalエラー
    override fun click() {}
    
}

デフォルトがfinalの利点は、スマートキャストが可能になることです。

ただし、abstractクラスのメソッドは継承前提なので、openは不要。

sealed class

暗黙的にopenのついているクラス。クラスの継承を制限するために使います。インタフェースにはsealedがつけられません。子クラスにdataクラスを持つこともできます。
whenでelse文で使うとelseが必要なくなります。まだあまり使い道が理解できてません。

クラス移譲 byキーワード

説明がわかりにくいです。記載してあるコードも動かないし。移譲に関しては、公式サイトを読みましょう。クラス移譲は、インターフェースの実装を他のクラスに委譲できます。
デザインパターンのデリゲートパターンですね。

{project_folder}/inaction/test.kt

interface Base {
    fun onClick()
}

class BaseImpl() : Base {
    override fun onClick() {
        System.out.println("clicked!!")
    }
}

class Application(base: Base) : Base by base

fun main(args: Array) {
    val b = Application(BaseImpl())
    Application(b).onClick()
}

オブジェクト宣言

シングルトンを作成する

5章

コレクション操作

mapは集約

filterはフィルタリング

遅延コレクション操作

一時オブジェクトを生成しないコレクションチェーンです。

mapは集約

SAM

関数が一つのjava interfaceはラムダで代用できる。

匿名objectでも代用できるが、呼び出しごとにインスタンスを生成する。

インスタンスを何度も生成しないで利用する場合は、以下のように実装する(p168)

{project_folder}/inaction/test.kt

vae listener = OnClickListener { view - >
  // something

}

ラムダ内でthisは使えない。なぜならコードブロックだから。

with

最初の引数を、二番目の引数のラムダレシーバに変換する。

{project_folder}/inaction/test.kt

fun alphabet = with(StirngBuilder()) {
  // something
toString
}

上記のような書き方も可能。。。

ラムダの実行結果を返す。
レシーバ(引数)を返したい場合は、applyを使う

apply

レシーバ(引数)を返したい場合に使う。TextViewで複数の値を設定する場合に利用する。(p172)

6章

エルビス演算子

監獄ロックだろ。知ってるよ。

null値の代わりにデフォルトを返す演算子です。

{project_folder}/inaction/test.kt

fun alphabet = with(aaa :Stvring?) {
  val msg: String? = aaa ?: "hello"
}

as?

null値の代わりにデフォルトを返す演算子です。

let

型のチェック

{project_folder}/inaction/test.kt

if (aaa != null) {
  mesoddo(aaa)
}

{project_folder}/inaction/test.kt

aaa?.let { mesoddo(it)}

関連記事

この記事がお役にたちましたらシェアをお願いします

このエントリーをはてなブックマークに追加

0 件のコメント:

コメントを投稿

Related Posts Plugin for WordPress, Blogger...