はじめに
AndroidStudioとIntelliJだと、一つだけメソッドを実装した匿名クラスが、いい感じ折り畳まれて表示され、読むときに非常にスッキリします!
匿名クラスはSAMインターフェース(後述)を実装したものでも良いですし、普通のクラスを継承して一つだけメソッドをオーバーライドした匿名クラスのインスタンスでもいいし、一つだけ抽象メソッドを持つ抽象クラスを継承した匿名クラスのインスタンスでも良いようです。
SAM型のインターフェースって?
SAMはSingle Abstract Methodの略、SAMインターフェースは、一つだけ抽象メソッドをもつインターフェースです。
SAM型のインターフェースのほんの一例を挙げると、
- java.lang.Runnable
- java.lang.Comparable
- android.view.View.OnClickListener
- android.location.GpsStatus.Listener
他にもたくさんあります。
冗長になりがちなJavaのコード
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.v(TAG, "clicked");
}
});
上記のコードは、クリックしたらログを表示するリスナーを設定しているコードです。
ViewクラスのsetOnClickListenerというメソッドを呼び出しています。
このメソッドは、View.OnClickListenerインターフェースを引数にとります。
ここでは、View.OnClickListenerインターフェースを実装した匿名クラスを生成して、それを引数に渡しています。
ここで本質的なものは何でしょうか?自分は次の3個だと思います。
- buttonという変数に対して
- Logを表示する
- リスナーをセットする
先ほどのコードは本質でなく冗長な部分が多いように感じます。
button.setOnClickListener(new View.OnClickListener() { // new以降本質でない
@Override // 本質でない
public void onClick(View v) { // あまり本質でない
Log.v(TAG, "clicked");
} // 本質でない
}); // 本質でない
コード中にコメントもしましたが、
- new View.OnClickListenerという記述
- Overrideアノテーション
- onClickメソッドというメソッド名
-
{}
や()
は本質ではないと思います。
Androidアプリケーション開発をはじめ、Javaの開発ではこのようなSAM型のインターフェースをUIのイベントリスナー・非同期処理のコールバックとして用いることが多いです。
リスナー、コールバックでの処理内容が本質なのに対して、このように冗長なボイラープレートなコードが、コードを読む際のノイズになってしまいます。
AndroidStudioとIntelliJだと、スッキリ!
AndroidStudioとIntelliJではでさきほどのコードは次のように表示されます。
button.setOnClickListener((v) -> {Log.v(TAG, "clicked");});
先ほど自分が上げた本質ではないという部分が、全て折り畳まれて表示されています。
冗長な部分、ボイラープレートなコードが隠されていて非常にスッキリしています。
コードを読む際、ノイズになる冗長な部分が隠されたことで、本質的な部分に集中することができ、可読性が上がっていると思います。
ちなみに2行以上中身がある場合は次のように表示されます。
button.setOnClickListener((v) -> {
Log.v(TAG, "clicked");
Log.v(TAG, "clicked");
});
また、書いた直後は折り畳まれませんのでご注意ください。
先の例はSAM型のインターフェースのView.OnClickListenerの例でした。次に、ViewPager.SimpleOnPageChangeListenerクラス(インターフェースでもなく抽象クラスでも、普通のクラス)の例を示します。
ViewPager.SimpleOnPageChangeListener listener = new ViewPager.SimpleOnPageChangeListener(){
@Override
public void onPageSelected(int position) {
Log.v(TAG, "on page selected : " + position);
}
};
このコードでは、ViewPager.SimpleOnPageChangeListenerクラスを継承して、onPageSelectedメソッドをオーバーライドした匿名クラスのインスタンスを生成しています。
ViewPager.SimpleOnPageChangeListener listener = new ViewPager.SimpleOnPageChangeListener(){
@Override
public void onPageSelected(int position) {
Log.v(TAG, "on page selected : " + position);
}
};
AndroidStudioとIntelliJでは次のように畳まれて表示されます。
ViewPager.SimpleOnPageChangeListener listener = onPageSelected(position) -> {
Log.v(TAG, "on page selected : " + position);
};
この機能を無効化したい場合
この機能を無効化したい場合は、
メニューアイテムの、
IntelliJ (もしくはAndroidStudio)
->Preferences
->Editor
->Code Folding
の"Closures"(anonymous classes implementing one method, before Java 8)
のチェックを外してください。
まとめ
Eclipseと比較してAndroidStudioのいい点を書いてる投稿はたくさん見ましたが、この理由に上げる投稿は、自分は見つけられませんでした。
もしかしたら嫌いな方もいらっしゃるかもしれませんが、この機能、個人的にはかなり好きです。
ここで畳まれる部分は冗長な記述であり、冗長なボイラープレートコードは、本当に大切な処理を読む際のノイズになってしまいます。冗長な部分が畳まれたことで本質的な処理を読むのに集中できますね。
おまけ GuavaのFunctional Idioms関連のクラス・インターフェースを使ってみる
Google製のJavaのUtilityライブラリ、Guava。
Functional Idioms関連のクラスの説明より(こちら)
Multiset<Integer> lengths = HashMultiset.create(
FluentIterable.from(strings)
.filter(new Predicate<String>() {
public boolean apply(String string) {
return CharMatcher.JAVA_UPPER_CASE.matchesAllOf(string);
}
})
.transform(new Function<String, Integer>() {
public Integer apply(String string) {
return string.length();
}
})
);
GuavaのFunctional Idioms関連を利用した上記のコードはリンク先では、読みづらい良くない例として紹介されています。ちなみにFunctional Idioms関連を使わないコードだと、下記のようになります。
Multiset<Integer> lengths = HashMultiset.create();
for (String string : strings) {
if (CharMatcher.JAVA_UPPER_CASE.matchesAllOf(string)) {
lengths.add(string.length());
}
}
圧倒的にFunctional Idioms関連を使わないコードの方が短く、読みやすい気がします。
無駄に長くなってしまうFunctional Idioms関連を使ったコード。これがAndroidStudioやIntelliJだと、どのように表示されるでしょうか?
Multiset<Integer> lengths = HashMultiset.create(
FluentIterable.from(strings)
.filter((Predicate) (string) -> {
return CharMatcher.JAVA_UPPER_CASE.matchesAllOf(string);
})
.transform((Function) (string) -> {
return string.length();
})
);
冗長な記述が無くなりある程度スッキリとはしました。
うーん。それでもまだ微妙ですね。
GroovyやScalaなどコレクションを扱うコードと比べてしまうとまだ冗長に感じてしまいます。