はじめまして!今年の4月にインフラエンジニアからAndroidエンジニアに転生したYoshihisaです。
先日開催されたShibuya.apk#20にて「kotlin界のsealed classはJava界からみても『sealed』なのか」というタイトルで5分LTをしました。
Kotlinには「sealed class」という継承範囲を同一ファイル内に制限する仕組みがあります。これをJavaから使おうとしたとき継承の範囲制限は有効なのか?を調べた結果をまとめたものです。*1
調べた動機と方法
そもそもこれをなんで調べたのかというと、YYCのAndroidアプリのある画面に新機能を追加する案件の開発中にAPI通信に関わる部分のモデルをsealed classで状態を定義したステートマシンとして作りそれをJavaで書かれたFragmentから使おうとしたことがあったからでした。
ふと「Kotlinの世界ではsealed classはsealedされていることをコンパイラがやってくれてるけどJavaの世界から見ると本当に『sealed』されているの?制限を突破して継承できたりしない?」と思ったのです。
気になったのでselaed classでクラスを作りそれをコンパイルしたバイトコードからJavaへ逆コンパイルする、というお手軽プランで調査してみました。(ついでにsealed classをJavaから使うコードを書いてみましたがif-else文とキャストまみれになり辛くなりました。Kotlinは同じコードをwhen式やif式、スマートキャストを駆使して簡単に書けます)
後日談
改めてsealed classを逆コンパイルした結果を見てみました。
sealed class本体のプライマリコンストラクタはprivateですがコンパイラがpublicなコンストラクタを自動生成しており子クラスはそれを使うようにバイトコードが生成されています。(スライド中では「DefaultConstructorMarkerが必要らしい」と書きましたが自動生成されたコンストラクタについて回るものらしくsealed class特有のものではありませんでした)
sealedを突破するためにDefaultConstructorMarkerをimportしようにもできず、またキャストなしでnullを渡してコンストラクタを呼び出すことも試みましたが存在しないことになっていて呼び出せませんでした。
コンパイル時はpublicなコンストラクタは存在しておらず呼び出せないので継承することができない、というような仕組みで守っているのでしょうか。もしこの記事を読まれた識者の方がいらっしゃればはてブのコメント等で教えていただけるとうれしいです。
今回は5分LTという形でしたが社外の勉強会で発表するのははじめてでした。とても緊張しましたが終わってみると楽しかったのでまたどこかで登壇できたらと思います。
宣伝
DiverseではAndroidエンジニアを募集中です!
興味がある方は @bomneko_attack または他の弊社エンジニアへお手軽にDMまたはリプライをください!
*1:sealed classそのものや使い途についてはid:kikuchyがKotlin Advent Calendar 2017 12日目でまとめているのでそちらをご参照ください。