Kotlinで作成したライブラリをJava向けに少し改良した
先日作成したロギング用のライブラリですが、Kotlin で記述してます。
さて、このライブラリ、Javaから使う場合はどのように見えるのか、Java向けの修正が必要なのはどこか調べてみました。
元のライブラリに関してはこちら
環境
- macOS Sierra 10.12.6
- Android Studio 3.0
Kotlinで作成したライブラリがJavaからどう見えるのか
今回作成したロガーは、KotlinのCompanion オブジェクトを使ってグローバルな設定をできるようになっています。
また、高階関数を使って実処理の前後に処理を挟むというような処理を行っています。
Kotlin からこのライブラリを使った場合、下記のようなコードになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
class KotlinActivity : AppCompatActivity() { private val logger = Logger(this.javaClass.simpleName) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_kotlin) // Companion へのアクセス Logger.tag = "LoggerSample" Logger.level = Logger.LOG_LEVEL_VERBOSE if(setupViews()) { logger.i("Succeeded to setup") } else { logger.e("Failed to setup") } } private fun setupViews(): Boolean { // 高階関数利用 logger.trace { logger.i("now setting up ....") return true } } } |
こんな感じのログが出力されます
1 2 3 4 5 |
D/LoggerSample: |DBG|KotlinActivity|setupViews start I/LoggerSample: |INF|KotlinActivity|now setting up .... D/LoggerSample: |DBG|KotlinActivity|setupViews end I/LoggerSample: |INF|KotlinActivity|Succeeded to setup |
これと同等のことをしたJavaコードがこちら
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
public class JavaActivity extends AppCompatActivity { private Logger logger = new Logger(this.getClass().getSimpleName()); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_java); // Companion へのアクセス Logger.Companion.setTag("LoggerSample"); Logger.Companion.setLevel(Logger.Companion.getLOG_LEVEL_VERBOSE()); if(setupViews()) { logger.i("Succeeded to setup"); } else { logger.e("Failed to setup"); } } private boolean setupViews() { // 高階関数利用 return logger.trace(new Function0<Boolean>() { @Override public Boolean invoke() { logger.i("now setting up ...."); return true; } }); } } |
だいぶイケてない感じになりました
- Companionは Companion という名前のスタティックなインスタンスに見える。
- プロパティアクセスは setter と getter が自動定義されているようなのでそれを使う。
- 高階関数は引数に応じたインターフェイスが定義されているのでそれを使う(Function22まであった)
Java向けのライブラリ改良
まずCompanion というインスタンスを挟むのがちょっと馴染めないので、Companion を挟まなくても良いように改良します。
可変なプロパティに関しては @JvmStatic
でCompanion を挟まずにアクセスできるようになります。
また、定数値に関しては const
修飾子をつけることで、Companion を使わず、getter もなくアクセスできるようになります。
1 2 3 4 5 6 7 8 9 10 11 12 |
companion object { const val LOG_LEVEL_NONE = 0 const val LOG_LEVEL_ERROR = 1 const val LOG_LEVEL_WARN = 2 const val LOG_LEVEL_INFO = 3 const val LOG_LEVEL_DEBUG = 4 const val LOG_LEVEL_VERBOSE = 5 @JvmStatic var tag = "AndroidLogger" @JvmStatic var level = LOG_LEVEL_NONE } |
ライブラリ改良後のJavaの呼び出しコードが下記。だいぶましになりました。高階関数に関しては、Javaが関数を第一級オブジェクトとして扱えない性質上、仕方ないかなと思います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
public class JavaActivity extends AppCompatActivity { private Logger logger = new Logger(this.getClass().getSimpleName()); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_java); // Companion へのアクセス Logger.setTag("LoggerSample"); Logger.setLevel(Logger.LOG_LEVEL_VERBOSE); if(setupViews()) { logger.i("Succeeded to setup"); } else { logger.e("Failed to setup"); } } private boolean setupViews() { // 高階関数利用 return logger.trace(new Function0<Boolean>() { @Override public Boolean invoke() { logger.i("now setting up ...."); return true; } }); } } |
ただし・・・実はJava の方だとtrace のログが意図しない感じになっているので、このあたりはもう少し改良の余地がありそうです。
1 2 3 4 5 |
D/LoggerSample: |DBG|JavaActivity|trace start I/LoggerSample: |INF|JavaActivity|now setting up .... D/LoggerSample: |DBG|JavaActivity|trace end I/LoggerSample: |INF|JavaActivity|Succeeded to setup |
関数名が trace になっちゃいました。
さいごに
2018年には Android の Kotlin のシェアは Java を上回る予測がでているそうです。
とはいえ、Javaがなくなるということは無いと思いますので、Kotlinでライブラリ作るにしてもJavaで使われる可能性は考えてコード書いたほうが良さそうですね。
最後まで読んでいただきありがとうございます。 このブログを「いいな」と感じていただけましたら、Twiter にてフォローいただけるとうれしいです。ブログ更新情報などもお届けします。
Follow @ryuta461
この記事をシェアする