Backlog Android: Java から Kotlin への楽しい乗り換え

featured

Kotlin バージョン 1.0 正式リリースをもうすぐに控え、この 楽しい プログラミング言語についてあなたが耳にする機会も増えたのではないでしょうか。Kotlin は私たちにコードを読みやすく改善する機会を与えてくれます。Kotlin は試しやすく、学習曲線も比較的上がりやすく、またJavaとの相互運用が100%な言語です。

ちょっと待って、まだバージョン1ではないけどすでに使い始めているの?

はい、昨年の11月に私たちは Kotlinで書かれた Backlog Android 1.2  をリリースしました。このリリースはKotlinで書かれた私たちの最初のプロダクトのとなりました。

Play Storeからアプリを手に入れよう

M12 (マイルストーン 12)から私たちは Kotlin に興味を持つようになりました。 Javaと100% 相互運用ができること は私たちが Kotlinにチャレンジするセーフティーネットとなりました。これは私たちのBacklog AndroidアプリをJava から Kotlin に切り替えるコストが0円になったということです (まあ会社ならコーヒーも無料ですからね 😛 ).

私たちの初めての Kotlin でのコードは Kotlin M12 でコンパイルされ、のちにリリースされたアプリは Kotlin 1.0.0-Beta2 でコンパイルされました。

私たちはその置き換えのプロセスにおいて ‘喜び’ と ‘悲しみ’ の両方を味わいました。KotlinのバージョンとAndroid Studio のライブラリーを新しくしたほとんどの場合は私たちのコードはシンプルなものになりました。 一つか二つの更新においてのみ、再びアプリを正常に動かすための余計な仕事が発生しました。

 

楽しさを始めよう

Adding support

Android Studio内にすでに存在しているAndroid プロジェクトにKotlin のサポートを追加する簡単な方法は、Kotlin クラスを作成しプロジェクトをKotlinプロジェクトとして再設定するよう促すことです。そして Android Studio の画面指示に従います。ようこそ Kotlin の世界へ。

プロジェクトの性質にもよりますが、 JavaのコードをAndroid StudioのKotlinプラグインの Code -> Convert Java File to Kotlin File メニューを使って変換し、その結果を必要に応じて修正するという方法もあります。もしくは Kotlin クラスをスクラッチで書くとか。

私たちの Java のコードにメソッドが多いほど楽しみも多くなります。
なぜなら、 Kotlin においてメソッド(function) 構文は fun から始まるからです。

Java class だと

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
class MyClass {
void myMethod() {
}
}
class MyClass { void myMethod() { } }
class MyClass {
  void myMethod() {
  }
}

というメソッドが Kotlin では以下のようになります。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
class MyClass {
fun myMethod(){
}
}
class MyClass { fun myMethod(){ } }
class MyClass {
  fun myMethod(){
  }
}

Kotlin のすてきなところ

ここで私たちが利用している Kotlin の幾つかの機能を紹介します。

Null Safety

Kotlin では、型付けシステムによってnull値を持てるもの (nullable references) と持てない (non-null references)型は区別されます。 これは私たちのコードから NullPointerException を減らし、コードの意図を理解しやすくしてくれます。 

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// 後でどこかで alphabet が null になることがある、ということがわかります。
var alphabet: String? = "abc"
// 後でどこかで alphabet が null になることがある、ということがわかります。 var alphabet: String? = "abc"
// 後でどこかで alphabet が null になることがある、ということがわかります。
var alphabet: String? = "abc"

Data class

data class を使うことで POJO classes を置き換えることができます.
例えば、Java の Customer POJO は

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
class Customer {
private String name;
private String company;
public String getName() {
return name;
}
public String setName(String name) {
this.name = name;
}
public String getCompany() {
return company;
}
public String setCompany(String company) {
this.company = company;
}
}
class Customer { private String name; private String company; public String getName() { return name; } public String setName(String name) { this.name = name; } public String getCompany() { return company; } public String setCompany(String company) { this.company = company; } }
class Customer {
  private String name;
  private String company;

  public String getName() {
    return name;
  }

  public String setName(String name) {
    this.name = name;
  }

  public String getCompany() {
    return company;
  }

  public String setCompany(String company) {
    this.company = company;
  }
}

ですが、Kotlin の data class に変換すると以下のようになります。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
data class Customer(var name: String, var company: String)
data class Customer(var name: String, var company: String)
data class Customer(var name: String, var company: String)

Multiple constructor

私たちはこの目的のために、二つの機能(default arguments and JvmOverloads annotation)と一緒に、custom view class で使用しています。
Java では複数定義した custom view のコンストラクタも

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
public class MyView extends View {
public MyView(Context context) {
super(context);
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
}
public class MyView extends View { public MyView(Context context) { super(context); } public MyView(Context context, AttributeSet attrs) { super(context, attrs); } public MyView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } }
public class MyView extends View {
  public MyView(Context context) {
    super(context);
  }

  public MyView(Context context, AttributeSet attrs) {
    super(context, attrs);
  }

  public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
  }
}

Kotlin では以下のようにまとめることができます。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
class MyView : View {
@JvmOverloads public constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0)
: super(context, attrs, defStyleAttr) {
}
class MyView : View { @JvmOverloads public constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : super(context, attrs, defStyleAttr) { }
class MyView : View {
  @JvmOverloads public constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0)
  : super(context, attrs, defStyleAttr) {
}

Function literals(lambda expression)

SAM(単一の Abstract メソッドしか持たない型)へ自動変換するこの機能は、多くの Android イベントリスナーを扱う上でとても便利です。

例えば以下のような Java の listener の処理を

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
doSomething();
}
});
button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { doSomething(); } });
button.setOnClickListener(new View.OnClickListener() {
  @Override
  public void onClick(View view) {
    doSomething();
  }
});

Kotlin の function literal では以下のように記述できます。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
button.setOnClickListener { doSomething() }
button.setOnClickListener { doSomething() }
button.setOnClickListener { doSomething() }

Extension functions

通常いくつかのコードでバージョン互換性を扱うことになりますが、 extension function 機能を使うことでそのコードを改善することができます。

Kotlin は継承を使うことなくクラスを拡張するための機能を持っています。これによってサードパーティー製のライブラリーを使う時にソースを書き換える必要がなくなるのでとても便利です。

例えば、API レベルチェックを append function を呼ぶたびに実行したい場合、以下のような appendSpecial extension を作成すれば良いのです。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
fun SpannableStringBuilder.appendSpecial(text: CharSequence, what: Any, flags: Int) {
if (Build.VERSION.SDK_INT >= 21) {
append(text, what, flags)
} else {
val start = length
append(text)
setSpan(what, start, length, flags)
}
}
fun SpannableStringBuilder.appendSpecial(text: CharSequence, what: Any, flags: Int) { if (Build.VERSION.SDK_INT >= 21) { append(text, what, flags) } else { val start = length append(text) setSpan(what, start, length, flags) } }
fun SpannableStringBuilder.appendSpecial(text: CharSequence, what: Any, flags: Int) {
  if (Build.VERSION.SDK_INT >= 21) {
    append(text, what, flags)
  } else {
    val start = length
    append(text)
    setSpan(what, start, length, flags)
  }
}

More time to play

現時点での一つのマイナスポイント(むしろプラス?)は Kotlin を使ったコンパイルは Java より遅いことです。時として何かうまくいかない時は full rebuild する必要があったりします。

Image via xkcd.com

Java interoperability

私たちはアプリをリリースしましたが、それはまだ 100% Kotlin で書かれているわけではありません。Java クラスが少し残っています。Java との相互運用性によって私たちは運用を続けながら少しずつ Java のコードを置き換えることができます。

Kotlin はここで書いたよりもっと多くの、私たちのコードベースを改善するための魅力的な機能があります。

まとめ

Kotlin は試しやすく学ぶのも難しくありません。今の時点で Kotlin は完璧でない部分があったとしても充分に使い物になります。コンパイルはJavaより時間がかかりますが。

使ってみて、もし問題があったとしても落ち着きましょう。Keep Calm.

Screen Shot 2015-12-17 at 10.23.02 AM

原文(Backlog Android: From Java to Kotlin With Lots of Fun

より良いチームワークを生み出す

チームの創造力を高めるコラボレーションツール

製品をみる