読んだ : Kotlin スタートブック ― 新しい Android プログラミング / 長澤 太郎 著
Kotlin エバンジェリスト (JetBrains 黙認) であり、日本 Kotlin ユーザグループ代表であるたろーさん (長澤 太郎) によって書かれた書籍 『Kotlin スタートブック ― 新しい Android プログラミング』 (赤べこ本) を読みました!!
Kotlinスタートブック -新しいAndroidプログラミング
- 作者: 長澤太郎
- 出版社/メーカー: リックテレコム
- 発売日: 2016/07/13
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (1件) を見る
本書を読む前から、公式のリファレンスやチュートリアルなどの気になる箇所を見てはいて Kotlin についてはなんとなく理解はしていたのですが、ところどころ知識が欠けている部分もあったので、本書によってそういった知識の欠落を埋めることができました。
どんな本か?
内容については著者のたろーさんが紹介されています。
導入としてまず 1 部があり、2 部で Kotlin の文法や機能の詳細が解説されます。 3 部にて、Android アプリ開発で Kotlin をどのように活用できるかが説明されます。 対象読者は 「Java および Android 開発経験者」 と書かれていますが、2 部までなら Android アプリ開発については知らなくても問題なく読み進められます。 「Android アプリ開発で Kotlin を使うために読む本」 というわけではなく、「Kotlin を使い始めるときに読む本」 かつ 「Android 開発に Kotlin を活用するためにどうすればいいかが書かれている本」 という感じです。
全体としては易し目の説明でスクリーンショットも多用されているのでプログラム開発の初心者でも読みやすい本だと思います。 一方で、説明する内容についてきちんと節が分かれていて飛ばし気味に読むこともやりやすいので、ソフトウェア開発についてある程度経験がある人が Kotlin の導入のために読むというのにも適しています。
感想
Kotlin を Java の代わりとして使い始めるのは結構簡単で、いくつかある Java との違い (各種文法の違いや 「==
演算子は同一性検査ではない」 みたいな違い) をおさえさえすれば導入できます。 逆に導入するのが簡単なせいで、「ちゃんと Kotlin 特有の機能や文法を学ぶ」 機会を設けず、必要に応じて公式リファレンスを参照している、という人も多いのではないでしょうか。 私もまさにそんな感じだったので、今回本書を読んで Kotlin 特有の機能、文法を学べたのは非常に良かったです。
内容的には深く突っ込んでいくという感じではないので、本書を読みつつ気になったところは公式リファレンスを参照する、みたいに読み進めていくといい気がします。 公式リファレンスとは違った視点での説明されている項目もあったりするので、両方参照することで理解が深まったりもすると思います。 公式リファレンスには書かれていない 『!!
演算子の代わりに requireNotNull
メソッドを使うと良い』 というような記述があるのも本書の良いところですね。
まさに 「これから Kotlin を使っていこうと思っている人のための Kotlin 入門本」 として、以下のような人にオススメできる書籍でした。
- Kotlin を使ってみてるけどまだ Kotlin 全体についてちゃんとは学んでいない。
- まだ Kotlin を使ってみてないけどこれから使ってみようかと思っている。
第 3 部の内容 (Kotlin で Android アプリ開発) について、ほとんど知っているものだったので自分にとってはあまり意味はなかったのですが、Kotlin を使った Android アプリ開発がどんな感じか全然知らない人にとってはそれらも役立つと思います。
読書メモ
自分にとって気になった箇所と、それに関連する公式リファレンスへのリンク。
require
メソッド で満たすべき条件を記述できる。 (P31)- string template と raw string 50
List
型の値がイミューダブルとはかぎらない。List
型のサブクラスにImmutableList
があるので、ImmutableList
オブジェクトがList
型として渡される可能性がある。 (P54)List
以外のコレクションクラスも同様。
Range
のin
演算子で、値が範囲に含まれているかどうか調べられる。 (P55)- イテレータに何らかのインターフェイスを実装する必要はない。 規定のメソッドさえオペレータとして定義されていれば良い。 (P63)
- 可変長引数は
vararg
修飾子で宣言。 配列を渡すときには spread 演算子 (*
; アスタリスク) で展開。 (P73) - 末尾再起呼び出し最適化に
tailrec
修飾子。 (P76) - ローカル関数、関数定義の中に関数を定義できる。 (P77)
- インライン関数の引数としてインライン関数外で呼ばれうるラムダを受け取る場合は
noinline
修飾子を付ける。 (P94)- 修飾子を何もつけないと、インライン関数の実行コンテキストでの実行しかできない。 インライン関数の引数のラムダもインライン展開されるため。
- スタックトレースの内容に影響を受けるような関数も
noinline
にすることを検討すると良いとのこと。 - 参考 : Inline Functions - Kotlin Programming Language
- インライン関数の引数として別の実行コンテキストでインライン化されて実行されるラムダを受け取る場合は
crossinline
修飾子を付ける。 (P94)- これにより、非ローカルリターンが使えないことを呼び出し側に伝える。
- 参考 : Inline Functions - Kotlin Programming Language
- インライン展開されるラムダ式では非ローカルリターンが使える。 (P95)
- ラムダ式から脱出する場合はラベルへのリターン。 (P96)
- Kotlin にはラムダ式以外に無名関数もある。 ラムダと違い
return
で無名関数から脱出する。 (P97) - ラムダ式と無名関数をあわせて関数リテラルという。 (P97)
- 公開範囲は public と internal (同一モジュール内) と private と protected (Java とは違って同一パッケージからは見えない)。 (P138)
- 使用場所変位指定による型投影。 使用側で
in
またはout
修飾子を指定する。 (P163) - クラス宣言における型パラメータが共変の場合は
out
修飾子を、反変の場合はin
修飾子で宣言。 宣言場所変位指定。 (P165) - 型引数について何もわからない場合でもそのジェネリック型を使いたい場合にはスター投影。 (P167)
- インライン関数の型パラメータを具象型パラメータ (reified type parameter) にすることで実行時にその型を扱えるようになる。 (P168)
!!
演算子の代わりにrequireNotNull
メソッドを使うことで意図をメッセージで表現できる。 (P180)- 安全キャスト
as?
を使うと、キャストできないときはnull
になる。 (P183) - 参照の等価性と構造の等価性。 (P190)
- 言葉遣いが 「同一性」 と 「等価性 (同値性)」 じゃないんだなー、という部分がちょっと驚き。
- 参考 : Equality - Kotlin Programming Language
infix
キーワードのついた 1 引数のメソッドを中置表記で呼び出せる。 (P192)- 範囲を作る
..
も中置表記。 - 参考 : Functions - Kotlin Programming Language
- 範囲を作る
- 分解宣言 (destructuring declaration) には
operator
つきのcomponentN
メソッドが必要。 (P194)- データクラスは componentN も自動生成してくれる。 (P197)
- 参考 : Destructuring Declarations - Kotlin Programming Language
- 内部クラスには
inner
修飾子を付ける。 (P199) try
は式。 (P211)- レシーバ付き関数リテラルの型
A.(B)->C
は(A, B)->C
のサブタイプ。 (P214)
Dropwizard + JDBI で SQL オブジェクトの返り値に Optional を使うときには SingleValueResult アノテーションが必要
Dropwizard で SQL ライブラリ JDBI を使うときのおはなし。
OptionalContainerFactory
dropwizard-jdbi ライブラリは OptionalContainerFactory
クラスを提供してくれていて、JDBI の SQL オブジェクトで返り値に Java 8 で導入された Optional
指定することができます。
ちなみに普通に dropwizard-jdbi の DBIFactory#build
メソッドを使うと自動的に OptionalContainerFactory
を DBI
に登録してくれるので、自分で登録する必要はありません。 (Dropwizard 1.0.5 で確認。)
SingleValueResult
アノテーションが必要 (単に Optional
を指定するだけでは動かない)
SQL オブジェクトのクラス定義で返り値に Optional
を指定すればそれだけで動くのかと思いきや、実はそんなことはありません。 Optional
を返すメソッドに SingleValueResult
アノテーションを付ける必要があります。
import info.vividcode.app.web.example.dropwizard.domain.Person; import org.skife.jdbi.v2.sqlobject.SqlQuery; import org.skife.jdbi.v2.sqlobject.customizers.SingleValueResult; import java.util.Optional; public interface PersonDao { @SqlQuery("SELECT * FROM person LIMIT 1") @SingleValueResult(Person.class) Optional<Person> findOne(); }
ここら辺のドキュメントがないので、ソースコードを読んで確認しました。
- 返り値に応じた
ResultReturnThing
を生成する箇所 : jdbi/ResultReturnThing.java at 3fe1480fcc3c42be94d355f8c7335bd784dbbc13 · jdbi/jdbi · GitHub SingleValueResult
アノテーションがあるかどうかを検査している箇所 : jdbi/ResultReturnThing.java at 3fe1480fcc3c42be94d355f8c7335bd784dbbc13 · jdbi/jdbi · GitHub
Optional
を使う場合に限らず、Iterable
じゃなくてコンテナを使いたい場合は SingleValueResult
アノテーションを付ける必要がありそうですね。
関連ページ
MySQL Connector/J 5.1 系では useLegacyDatetimeCode=false にしよう
JDBC で MySQL に接続するときに使用する MySQL Connector/J (mysql:mysql-connector-java) の話。 サーバー・クライアントのタイムゾーン設定が違っている場合にどう対応するのがいいか。
結論
- MySQL Connector/J 6 (まだ開発版だけど) 以降は自動でやってくれるので気にする必要はない。
- MySQL Connector/J 5.1 では URL に
useLegacyDatetimeCode=false
を入れて、時刻周りの新しい処理が動くようにしろ。- 新しい処理では、タイムゾーンの変換を一貫性をもってやってくれるようになる。
- 『Use code for DATE/TIME/DATETIME/TIMESTAMP handling in result sets and statements that consistently handles time zone conversions from client to server and back again』
- 参考 : 5.1 Driver/Datasource Class Names, URL Syntax and Configuration Properties for Connector/J
- 5.1 系ではデフォルトでは互換のために新しい処理は動かないようになっているので、明示的に新しい処理を使うように URL で指定する必要がある。
- バージョン 5.1.6 で導入された機能なので、それより古いものでは使えない。
問題
そもそもどういう問題に遭遇したのか。
- Java のアプリケーションサーバーのタイムゾーンが JST。
- MySQL サーバーのタイムゾーンが UTC。
- Java アプリケーションから MySQL サーバーには MySQL Connector/J 5.1 系で接続。
- タイムゾーン周りのオプションは何も指定せず。
- SQL 文の
NOW()
関数やDEFAULT CURRENT_TIMESTAMP
で設定された時刻を Java アプリケーション側で取得すると、現在時刻から 9 時間前の時刻が返ってきた。 - → MySQL Connector/J がサーバー・クライアント間のタイムゾーン差を扱ってくれてない。
MySQL Connector/J とタイムゾーン
- もともとは
useTimezone
プロパティやserverTimezone
プロパティを使って対応する必要があった。 - MySQL Connector/J 5.1.6 で時刻周りの処理が書き直されて、
useLegacyDatetimeCode=false
することでタイムゾーン変換などを自動で扱ってくれるようになった。 - MySQL Connector/J 6 では
useLegacyDatetimeCode
プロパティを含め、古いタイムゾーン周りのプロパティは全部削除される。
おわり
タイムゾーンはライブラリ側がちゃんと面倒見てくれるだろう、と思って気にしなかったら、環境を変えて Java アプリケーションと MySQL サーバーのタイムゾーン設定がずれたときにいきなり想定しない動作になったりするので気を付けましょう。
grunt-ts が .baseDir.ts ファイルを作るのを抑制する
Grunt を使っていて TypeScript のビルドを行うタスクを定義する際には grunt-ts を使うことが多いでしょう。 (grunt-typescript もあるけど。) grunt-ts で困ったことがあったので書いておきます。
outDir
オプションを使うと .baseDir.ts ファイルが作られる問題
grunt-ts を使って tsc
コマンドの --outDir
オプション指定を行うには outDir
プロパティが使えるのですが、この値を指定すると、何故か 「.baseDir.ts」 という名前のファイルがソースディレクトリの方に生成されてしまいます。
このファイルは何かというと、TypeScript 1.5 で --rootDir
オプションが導入される前に grunt-ts 独自に --rootDir
相当のことをするために導入されたものです。 grunt-ts のドキュメントを読む限り、baseDir
オプションを使っておらず、fast コンパイルが無効になっている場合には 「.baseDir.ts」 ファイルは作られないはずです。 しかしながら、grunt-ts 5.5.1 で試したところ、baseDir
オプションを使わず fast
オプションに "never"
を指定しても outDir
オプションを指定すると 「.baseDir.ts」 ファイルが作成されてしまいました。 個人的にはバグっぽいなーと思っています *1。 一応 pull request を投げておきました。
ちなみに以下のような Gruntfile.js (一部) で動作確認しました。
grunt.initConfig({ ts: { default: { src: ["src/ts/**/*.ts"], outDir: "build/ts" options: { fast: "never" } } } });
回避方法
現在 (バージョン 5.5.1) の grunt-ts に .baseDir.ts ファイルを作らせないようにする方法はいくつかあります。
outDir
オプションではなく additionalFlags
で --outDir
オプションを指定する
grunt-ts は outDir
オプションを見て 「.baseDir.ts」 ファイルを生成するかどうか決めているので、additionalFlags
の方で --outDir
を指定すると 「.baseDir.ts」 ファイルは生成されません。
grunt.initConfig({ ts: { default: { src: ["src/ts/**/*.ts"], options: { additionalFlags: "--outDir build/ts" } } } });
tsconfig.json ファイルを指定する
tsconfig.json の中で outDir
オプションを指定し、grunt-ts には tsconfig
プロパティで tsconfig.json のパスを指定教えるだけにするという方法もあります。 ただし、普通に指定するだけでは tsconfig.json を解析して grunt-ts が余計なことをするっぽい (tsconfig.json 側で outDir
オプションを指定していると、grunt-ts が .baseDir.ts ファイルを作ってしまう) ので、passThrough
オプションを有効にして、grunt-ts が余計なことをしないようにする必要があります。
grunt.initConfig({ ts: { default: { tsconfig: { tsconfig: "src/ts/tsconfig.json", // このファイルの中でコンパイル対象のファイル群や `outDir` 指定を行う passThrough: true } } } });
おわり
こんなしょうもないことをやってるのは無駄だと思うし grunt-ts はオススメしません。
関連ページ
- Stop creating .baseDir.ts file · Issue #300 · TypeStrong/grunt-ts · GitHub :
rootDir
オプションが有効なら .baseDir.ts ファイルを生成しないとかしてもいいのでは、みたいな話がされてる。 - TypeScript needs a `baseDir` option · Issue #77 · TypeStrong/grunt-ts · GitHub :
baseDir
オプションが導入されることになった経緯。
*1:ドキュメントの記述から察するに仕様ではなさそうだし、コードの実装も微妙に変なので
UWP アプリ 「みお×ぽん」 のバージョン 1.1.0 をリリースしました
2016 年 10 月 1 日、IIJmio の 「IIJmio 高速モバイル / D サービス」 が au の回線にも対応して 「IIJmio モバイルサービス」 に改称されました。
それに合わせて、「IIJmio モバイルサービス」 のクーポン切り替えを行うことができる UWP アプリ 「みお×ぽん」 をバージョンアップしました。
文言変更や一部デザイン変更、バグ修正のみであんまり大きな変更はしていないのですが、ご利用くださっている方はバージョンアップしてくださいませ。
変更内容
ちなみに公式アプリもあるよ
「みお×ぽん」 の最初のリリース時には IIJ による公式の UWP アプリはなかったのですが、2016 年 6 月ごろに公式アプリがリリースされました。
先ほど、Windows10版(UWP版)のIIJmioクーポンスイッチアプリ(みおぽん)をリリースしました。Windows10のインストールされたPC、およびWindows10 Mobileのスマートフォン等でご利用になれます。https://t.co/rawRYSL0wX
— IIJmio (@iijmio) June 3, 2016
やったぜ!
*1:昔の MS のサンプルプロジェクト通りの実装だとだめっぽい。 これに関してはまた書こうと思う。