Yabu.log

ITなどの雑記

Java読書会BOF「Kotlinイン・アクション」を読む会 第4回に参加

5章の終盤から7章の途中まで読みました。 終盤に近づくにつれてメモが少なくなるのは仕様です

Kotlinイン・アクション

Kotlinイン・アクション

感想

結構大事なKotlinでNullを扱うときの話、Kotlinの型の話を含む6章を中心的に読みました。 今回の内容は前回よりは優しかったが、実際にどのように動いているのかわからない箇所が何箇所かあった。 swift(Objective-C)やKotlin(Java)のような従来のエコシステムとの共存をしている言語はコンパイラがかなり頑張って調整しているらしい。 Kotlinはnullがかなり扱いやすくなっているが、アノテーションなどをしっかりつけていないJavaのコードと混ぜて使った途端に、実行時エラーのリスクが上がったりで魅力半減だと個人的に思いました。状況が悪くなっているわけではないので特に問題ないと思いますが、そのようなプロジェクトではJavaのコードを少しづつKotlinに置き換えることで幸せになれるのかなと思いました。

連絡など

5.4.2 SAM変換

よくわからん。

レシーバ、レシーバ付きラムダなどの用語の整理が必要。

with

  • 呼び出しを減らす工夫。
  • 初出は?delphi?
  • vbにもある

  • delphijavascriptなどではwithは危険なので非推奨。という流れらしい。

  • vbは.を付けるつけないで振る舞いが変わるので注意(ハマった。)
  • fileInputStreamをfisと略すのはヤメテ。
  • javaScriptではwithは言語構文。strictモードではwithは使えない。
  • JSのwithはパフォーマンス的にもよくないという言い伝えが。。。
  • Kotlinではブロックは全てラムダ?
  • withのような省略して書ける機能は色々問題とのトレードオフ

buildString関数。

洗練された方法らしい。

拡張関数

  • どこに書かれるかわからない。保守性が下がる。
  • 一見便利だけどあとで死ぬ。

第6章 Kotlinの型システム

6.1 null許容性の区別

  • null許容型はkotlinでは明示的に指定する必要がある。
  • null許容型を使った場合、様々な制限が発生する
  • null型の引数のメンバ参照ができない
  • null許容型は非許容型の変数に代入ができない
  • Kotlinではnullを促進しているように感じる(普段javaではnull撲滅派の人の意見)

型の意味

  • JavaのString型は文字とnullの2種類の値を持つことができる。
  • が、文字とnullでは使える操作に差がある。
  • 文字型の値しか持てないKotlinのStringは優秀。

Kotlin風のnullチェック

foo?.bar() fooがnullならnullが帰る。違うならfoo.bar()が帰る。

if(foo != null) foo.bar() else null に相当。

エルビス演算子

なんか難しそうな数学者の名前とかではなく、エルビスプレイスリーが元らしい(笑)*1

val t: String = s ?: ""

安全キャスト演算子:as?

非null表明

//sがnullの場合NullPointerExceptionが発生するval sNotNull: String = s!!

p188タイポか

p.188下部

isEnabledがよりも

letの拡張関数としての実装

  • ジェネリクスに対してletを拡張関数として実装されている。
  • どんな型にも拡張関数を付け足せるじゃないか!と話題になりました。
/**
 * Calls the specified function [block] with `this` value as its argument and returns its result.
 */
@kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(this)
}

安全呼び出しletで式がnullの場合

nullが帰る(let関数の中身が実行されないだけではなくnullと評価される)

遅延評価のnull非許容型

lateinitキーワード - 依存性の注入に使う

null許容型の拡張関数

実装は以下のようになっていた

/**
 * Returns `true` if this nullable char sequence is either `null` or empty.
 */
@kotlin.internal.InlineOnly
public inline fun CharSequence?.isNullOrEmpty(): Boolean {
    contract {
        returns(false) implies (this@isNullOrEmpty != null)
    }

    return this == null || this.length == 0
}

型パラメータをnull非許容型にする必要

  • 型パラメータは?をつけなくともnull許容型になってしまうので注意が必要。
  • Any?ではなく、Anyのサブクラスであることを明記する。
fun<T: Any> printHashCode(t:T){
  println(t.hashCode())
}

型パラメータは、null許容型の定義には最後に疑問符が必要であり、疑問符がない型はnull非許容型であるというルールに対しての唯一の例外です。

ジェネリクスだけ特殊なので、Kotlinで型とnullを考えるときは注意する必要がある。

Javaと同時に使った時とKotlinのnull

アノテーションがついた変数が非null型/null許容型に変換可能。

@Nullable + Type = Type? @NotNull + Type = Type

アノテーションがない場合はプラットフォーム型に変換される(Type!) 実行時にNullPointerExceptionが出てくる。プログラマが実装に責任を持つ必要がある。

  • 全部null許容型にすれば安全そうだが、以下のような場合に冗長なnullチェックが必要になってしまう
ArrayList<String>

kotlinでは

ArrayList?<String?>

という扱いになってしまう

Javaのクラスをオーバーライドするときのnullの扱い

public interface StringProcessor {
    void process(String value);
}

null許容型、null非許容型どちらでもオーバーライドできる

class StringPrinter : StringProcessor{
    override fun process(value:String){

    }
}
class StringPrinter2 : StringProcessor{
    override fun process(value:String?){

    }
}

どちらでもコンパイル可能。

6.2 プリミティブ型と基本的な型

  • Kotlinではプリミティブ型と基本的な方との違いはない?
  • KotlinからみたJavaのプリミティブ型はnull非許容型になる。
    • Javaのint/charはnullを扱えないので当たり前と言っちゃ当たり前。
  • javaではnullをプリミティブ型で保持できないのでKotlinでnull許容型は必ず対応するラッパークラスになる。(Integer/Double等)

Kotlinでもプリミティブ型は存在している(非効率的なので全てを参照型(ラッパークラス)としているわけではない)

Kotlinは暗黙の型変換を許さない

暗黙の型変換は各方面で色々問題を起こしている

java 16進数浮動小数リテラル

マニア知識 https://stackoverflow.com/questions/25712348/hexadecimal-floating-point-literals 0.1などを実数で表せる。

Void(Java),Unit,Nothing等

  • Void型:Javaで総称型のメソッドで何も返さない時に指定(ただし冗長なreturn nullが必要)
  • Unit:JavaのVoid型に相当。ただしreturn nullの部分は自動生成されるのでソースに記述する必要はない。
  • Nothing:正常に帰り値を戻さないことを明記する。
    • 例外や割り込みなどで終了することが期待されるメソッド,
    • もしくはそもそも終わる予定のない無限ループするメソッド用

6.3 コレクションと配列

  • 読み取り専用コレクションはMutableコレクションの引数として渡せない
  • 読み取り専用コレクションは必ずしもいミュータブルではない
  • 読み取り専用コレクションはスレッドセーフではない

読み取り専用コレクションの扱いがちょっと微妙。プログラマが色々責任を持つ必要がある。

KotlinとJavaでのMutable Collectionの互換性

パッケージの改装関係などで、型階層上、互換性がないが、typealiasという特殊な仕組みでインターフェースの差分がmapされている?

@SinceKotlin("1.1") public typealias ArrayList<E> = java.util.ArrayList<E>

よくわからない言葉メモ

  • インタープリベータリィ?
  • インターフェースオペラビリティ?

ちなみにMutableListのプライマリコンストラクタは以下のような実装になっている。

@SinceKotlin("1.1")
@kotlin.internal.InlineOnly
public inline fun <T> MutableList(size: Int, init: (index: Int) -> T): MutableList<T> {
    val list = ArrayList<T>(size)
    repeat(size) { index -> list.add(init(index)) }
    return list
}

MutableListのInterfaceも存在している。ただしKotlinがどのような仕組みを使ってJavaArrayListをMutableListの

第7章 演算子オーバーロードとその他の変換の規約

7.1 算術演算子オーバーロード

Kotlinでは算術演算子オーバーロードできる。 例えばBigIntegerクラスの加算はaddメソッドではなく+でできる方が可読性が高いかもしれない。

operatorキーワドを利用し演算しに対応する関数名をつける。

operator fun plus(other:Point):Point{}
演算子 関数名
* times
/ div
% mod
+ plus
- minus

演算子の優先順位は変えられない。 Javaからは関数名を使って通常の関数名として呼び出し可能。

  • 可換性()は自動でサポートされない。
  • 演算はオペランドの型は同一出なくても良い
  • 結果の型はオペランドの方と同一でなくても良い
  • ビット演算しは定義されていないので、別途定義された関数を使う。

    複合代入演算子

+=のようなもの ミュータブルなオブジェクトのみに実装可能。(ビルダーのような) ++=は両方サポートできない。

単項演算子オーバーロード

関数名
+a unaryPlus
-a unaryMinus
!a not
++a a++ inc
--a a-- dec

7.2 比較演算子オーバーロード

  • compare,equalsなどの代わりに使用可能。
  • Kotlinでは==を使うと自動でequals()が呼び出される*2
  • Dataクラスの場合はeqaulsは自動実装される
  • Kotlinの===はjavaの==と同等であることに注意
  • ===はオーバーライドできない。
  • !=は!equals()が呼ばれる

7.2.2 順序づけの演算子:compareTo

comparableが実装しているJavaクラスは特に容易なしに演算しで比較可能

println("abc"< "bac")//true

7.3 コレクションやレンジに使われる規約

  • val value= map[key]//get():Tが呼ばれる
  • map[key] = newVal //set(T)が呼ばれる
  • in演算子contains()が呼ばれる

次は7.3.3から!

*1:頭を左に傾けてみると?がリーゼントに見えるから

*2:規約通りequalsが実装されている場合は