はじめに
OWNR by RENOSY Android開発のSugeunです。🤓
OWNR by RENOSYは海外のお客様向けに英語対応を始めました。
今回はKotlinにはstatic修飾子がなかったので、代わりとして@JVMアノテーション, companion Objectの使い方を勉強しました。
@JVMアノテーションとcompanion Objectはkotlinのパッケージ内部の関数を宣言してStaticとして扱うことができます。
Staticとは。
Staticの辞書的な意味としては静的な、動いていない
の意味としてメモリ上に固定されていて全てのオブジェクトに共有されると言う意味です。
アプリ全体で主に使われる変数、メソッドをStaticと指定して扱います。
Staticが無いKotlinで対案としては変数、メソッド全てをObjectとして扱う方法もあります。
// kotlinでObjectとして扱う。 object Hoge { val hoge : String = "hoge" fun fuga() : String { return "fuga" } }
だけどこれをStaticの対案として呼ぶのもおかしいです。
Objectの概念はStaticよりSingletonの概念に近いからです。Staticの対案としてパッケージ内部の関数を使うようにしましょう。
@JvmField アノテーションを使ってClass 変数宣言。
Class内部の変数をClass 変数として使うことができます。
JavaからKotlinの変数を呼ぶ場合には@JvmField アノテーションを使うことで自動にgetterを付けてくれます。
// kotlinで@JvmFieldを導入する。 class ExampleActivity : AppCompatActivity() { @JvmField val hoge : String = "hoge" val fuga : String = "fuga" } // @JvmFieldの変数を呼び出す。 ExampleActivity.hoge // "hoge"
// JavaからKotlinの変数を呼び出す場合。 public class AboutJavaClass { String hoge = ExampleActivity.hoge; String fuga = ExampleActivity.Companion.getFuga(); }
companion objectを使ってブロックをStatic化する。
Classの内部でcompanion objectを宣言してブロック内部の変数とメソッドを各々Class 変数、Classメソッドとして使えうことができます。
companion object内部のClassメソッドとして扱う時@JvmStatic
アノテーションを付けても付けなくても大丈夫です。
companion object { }
をcompanion object Example { }
みたいに名前を付けて使用することもできますがそもそもcompanion objectはクラスで一つしか存在しないので個人的に名前を付けずに使うことをお勧めします。
// kotlinでcompanion objectを導入する。 class ExampleActivity : AppCompatActivity() { companion object { const val hoge : String = "hoge" @JvmStatic // @JvmStaticは省略可能です。 fun fuga() : String { return "fuga" } } } // companion objectを呼び出す。 ExampleActivity.hoge //"hoge" ExampleActivity.fuga() //"fuga"
const : Kotlinで使われる変数の属性で、JavaのFinalとPrivateの属性を合わせた関数です。
初めてAndroidを勉強した時、なぜCompanionObjectを使うのかよくわからなかったんですが companionObjectを使う時と使わなかった時の区別でその違いをわかるようになりました。
下記はActivityからFragmentにデータを渡してViewをReplaceするときよく使ってるコードです。
// companion objectを使わなかった場合のコード // NoCompanionObjectActivity class NoCompanionObjectActivity : AppCompatActivity() { val stringKey = "StringKey" val intKey = "IntKey"" val fragment = NoCompanionObjectFragment() // Bundleにパラメータ値をsetする。 val bundle = Bundle().apply { this.putString(stringKey, "hello") this.putInt(intKey, 1) } fragment.arguments = bundle supportFragmentManager.beginTransaction() .replace(R.id.containerFrame, fragment) .commit() } // NoCompanionObjectFragment class NoCompanionObjectFragment : Fragment() { val stringKey = "StringKey" val intKey = "IntKey" override fun onViewCreated(view: View, savedInstanceState: Bundle?) { // Bundleでパラメタ値をgetする。 } }
// companion objectを使った時のコード // CompanionObjectActivity class CompanionObjectActivity : AppCompatActivity() { val fragment = CompanionObjectFragment.newInstance("hello", 1) supportFragmentManager.beginTransaction() .replace(R.id.containerFrame, fragment) .commit() } // CompanionObjectFragment class CompanionObjectFragment : Fragment() { companion object { private const val StringKey = "StringKey" private const val IntKey = "intKey" fun newInstance(greeting: String, number: Int) = CompanionObjectFragment().apply { // Bundleにパラメータ値をsetする。 arguments = Bundle().apply { putString(StringKey, greeting) putInt(IntKey, number) } } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { // Bundleでパラメタ値をgetする。 } }
私がこのロジックを見て考えたcompanion object
を使う利点として
ActivityはFragmentに渡したいパラメータを渡すだけで良いです。 渡したパラメータをbundleに入れたり、argumentで出したりする作業をFragmentで行うのでActivityが気にすることはないのです。
argument keyをFragmentで管理することができます。
最後に
companion objectはFragmentから新しいInstanceを作る時使ったことはありましたが @JvmField、@JvmStaticは使う機会がなかったので今回まとめてみました。
Kotlinの言語だけで開発を行う場合は基本Companion Objectで大丈夫ですがJavaからKotlinのmethodとか変数を呼び出す場合は@Jvmstatic,@JvmFeildを是非明示してください。
今後もよろしくお願いします。😸