演算子オーバーロードを使ってFragmentを生成しない方が良いのではないか

少しツイートが古いですが、Kotlin + AndroidのFragmentで下記ツイートの方法を使っているコードを見かけたので自分の考えを書いておきます
※このツイートがどのような文脈で呟かれたのかを確認していないのでこのツイートだけを見て思った感想です。

class MyFragment : Fragment() {
    companion object {
        operator fun invoke(arg: Int): MyFragment {
            val frag = MyFragment()
            frag.arguments = Bundle().also { it.putInt("ARG", arg) }
            return frag
        }
    }
}

Fragmentを生成する際の問題

前提知識として、AndroidのFragmentを生成する際には以下のような問題点があります。

  • コンストラクタで値を渡すだけではFragmentが再生成された時にその値が復元されないためFragment#setArguments(Bundle)を介して値を渡す必要がある
  • 再生成する際にデフォルトコンストラクタ(引数なしのpublicなコンストラクタ)が必要
  • デフォルトコンストラクタ以外のコンストラクタを定義すると警告が出る f:id:scache:20171205020555p:plain

何ができるようになるか

まず、冒頭のコードを書くことで何ができるかというと以下のようにコンストラクタを呼び出すかのようにFragmentを生成できます。さらに問題点であった警告も表示されません。

MyFragment(42)

Fragmentで演算子オーバーロード(invoke)を使う際の問題

Fragmentではデフォルトコンストラクタが必要という制限があるためMyFragment(Int)だけではなく、MyFragment()の呼び出しも可能になるという問題があります。
もちろんデフォルトコンストラクタで正しくFragmentを生成できるよう実装をしていれば問題はないのですが、そのような場面はあまりないのではないでしょうか?

間違えて、正しくFragmentを生成できないコンストラクタを使ってしまう可能性があるため、冒頭のようなコードは書かずにnewInstanceのようなファクトリメソッドを定義するのが良いと個人的には思います。

最後に

今回のFragmentのケースではAndroidFrameworkの制約があるため、あまり良くない演算子オーバーロードの使い方だと感じました。
色々議論の余地はあると思うのでぜひ意見があればコメントなどお願いします。