はじめに
こんにちは。引き続きKotlinの基本文法で、今回はクラスのネストについてです。
JavaやSwiftにもある文法ですが、やはりよく理解せずに何となく使っていましたので、改めてきちんと理解したいと思います。
クラスのネスト
以下のコードはtry kotlinで試しています。クラスの中に新しいクラスを記述すればよく、特に難しいことはないと思います。
なお入れ子の階層に制限は無く、いくつでもネスト可能です。やり過ぎても可読性を落とすだけでしょうが…
1 2 3 4 5 6 7 8 9 10 11 12 13 | fun main(args: Array<String>) { println(Sample.Nested().msg) println(Sample.Nested.MoreNested().msg) } class Sample() { class Nested() { val msg: String = "nested class is running." class MoreNested() { val msg: String = "more nested class is running." } } } |
入れ子のクラス
Nested
や
MoreNested
に
ptivate
を付ければ、
Sample
クラスからのみアクセス可能になります。
内部クラス
Kotlinでは
inner
キーワードを付けることで、内部クラスから外側のクラスのメンバーにアクセスできるようになります。
Androidではイベントリスナーの実装によく用いられています。下記コードではボタンクリック時と長押し時の処理を実装していますが、一つのイベントリスナーにつき一つの内部クラスとして処理が切り分けられており、リスナーが増えた場合でも可読性を保ちやすくなっています。
また、リスナーの中からMainActivity.messageを直接参照できるのもいいですね。
(AndroidStudio3.2.1, Kotlin1.2.71)
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 | class MainActivity : AppCompatActivity() { lateinit var message:String override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val button:Button = findViewById(R.id.button) button.setOnClickListener(ButtonClickListener()) button.setOnLongClickListener(ButtonLongClickListener()) } private inner class ButtonClickListener: View.OnClickListener { override fun onClick(v: View?) { message = "ボタン id:${R.id.button} が押されました" Toast.makeText(applicationContext, message, Toast.LENGTH_LONG).show() } } private inner class ButtonLongClickListener: View.OnLongClickListener { override fun onLongClick(v: View?): Boolean { message = "ボタン id:${R.id.button} が長押しされました" Toast.makeText(applicationContext, message, Toast.LENGTH_SHORT).show() return true // falseを返すとOnLongClickListenerの後にOnClickListenerも呼ばれる } } } |
レイアウトのXMLは以下の通りです(ボタンを1個置いているだけですが…)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/button" android:text="TEST" /> </android.support.constraint.ConstraintLayout> |
- 2行目のlateinitは前回の記事で紹介しました。このように、インスタンス生成時より後にプロパティを定義しつつ、最初からnon-nullで書けて便利です。
-
private
修飾子を付けることで、MainActivityからのみ呼ばれることが明示されています。コードの読み手にも実装の意図が伝わりやすくなりそうです。
Javaコードとの比較
Javaで同様の実装をすると、以下のようになります。
1個のボタンに2つのリスナーを実装する程度では複雑にならず、読みづらくもありません。
ただ、さらに多くのイベントリスナーや他の処理が加わると、onCreateが肥大化していってしまうため、MainActivityにOnClickListenerインターフェースを実装するなどして、処理を切り分ける必要がありそうです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | public class MainActivity extends AppCompatActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button mButton = findViewById(R.id.button); mButton.setOnClickListener(new View.OnClickListener(){ public void onClick(View view){ String message = "ボタン id:"+R.id.button+"が押されました"; Toast.makeText(MainActivity.this, message, Toast.LENGTH_LONG).show(); } }); mButton.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View mView) { String message = "ボタン id:"+R.id.button+"が長押しされました"; Toast.makeText(MainActivity.this, message, Toast.LENGTH_LONG).show(); return false; } }); } } |
さいごに
いかがでしたでしょうか。今回例示したイベントリスナーの実装方法は他にもありますが、内部クラスを使った書き方は処理の切り分けがコードを一見しただけで判別しやすく、読み手に優しいコードを維持しやすいと感じました。