Edited at

Kotlin覚書 - 複数の総称型の場合と、上限制約 @Generics

More than 1 year has passed since last update.


KotlinでGenerics

自分の無知さを痛感して、改めてDialogFragmentについて実装しているときに、以下のコード展開されている投稿を発見しました。

https://qiita.com/kojionilk/items/9584c012679f61569995

staticなBuilderクラスをMyDialogFragmentに実装して、ActivityやFragmentからの場合はthisを渡すだけで簡単にダイアログ表示できるようなものです。

これはいい!でもJavaで書かれてる!!!

ということで、Kotlin化してみようと思ったら、案の定つまづきました。


引数の総称型が違うコンストラクタ


java

public class Test {

private AppCompatActivity fieldActivity;
private Fragment fieldFragment;

public <A extends AppCompatActivity & DialogActionInterface> Test(final A activity) {
fieldActivity = activity;
}

public <F extends Fragment & DialogActionInterface> Test(final F fragment) {
fieldFragment = fragment;
}
}


コンストラクタの引数に対しての総称型が違うパターンです。

ちょっとわからないので、必殺Java→Kotlinへのコンバートをしてみました。


kotlin

class Test {

private val fieldActivity: AppCompatActivity
private val fieldFragment: Fragment

constructor(activity: A) { // GenericsのAの型指定がないためエラーになってる
fieldActivity = activity
}

constructor(fragment: F) { // GenericsのFの型指定がないためエラーになってる
fieldFragment = fragment
}
}



結果→失敗

うまくいかないようです。Genericsの型指定がコンバートするとどこかにいってしまいました。

こりゃこまった。

やはりここはリファレンスを読もうということで、読みました。

https://kotlinlang.org/docs/reference/generics.html

総称型については、classで指定してあげればいいようです。


Kotlinでの方法


kotlin

class Test<A:AppCompatActivity, F:Fragment> {

private var fieldActivity: AppCompatActivity?
private var fieldFragment: Fragment?

constructor(activity: A) {
fieldActivity = activity
}

constructor(fragment: F) {
fieldFragment = fragment
}
}


元のクラスで指定してあげればいいのでした。

Genericsの場合は使用方法も他に書いてあったので、場合によってうまく使い分けられそうです。

参考:https://qiita.com/k5n/items/18adb5c3503a54e96c22

inやoutなどの使い方があり、総称型の定義をする前につけてあげればいいようです。


kotlin

class Test<in A: AppCompatActivity, in F:Fragment> {

private var fieldActivity: AppCompatActivity?
private var fieldFragment: Fragment?

constructor(activity: A) {
fieldActivity = activity
}

constructor(fragment: F) {
fieldFragment = fragment
}
}


↑だと、引数としてのみの使用であればinをつけます。

逆に、戻り値としてのみ使用する場合はoutをつけます。

さて、これで総称型AとFについて、型の指定ができました。

が、まだ元のJavaコードとは差異があります。


上限制約

一番上のJavaコードをもう一度みてみましょう。


java

public class Test {

private AppCompatActivity fieldActivity;
private Fragment fieldFragment;

public <A extends AppCompatActivity & DialogActionInterface> Test(final A activity) {
fieldActivity = activity;
}

public <F extends Fragment & DialogActionInterface> Test(final F fragment) {
fieldFragment = fragment;
}
}


2つのコンストラクタで渡す引数がそれぞれ違うのと、さらに総称型に指定している型が複数です。

こういうときの扱いをよくわかってないため、@ngsw_taroさんの

http://kotlin.hatenablog.jp/entry/2012/12/17/234300

こちらの記事を参考に勉強させてもらいました。

こういう上限制約の場合、kotlinではwhere を使います。

whereで上限制約をして、総称型の指定を複数にします。


kotlin

class Test<A, F> where A:AppCompatActivity?, A:DialogActionInterface?, F:Fragment?, F:DialogActionInterface? {

private var fieldActivity: AppCompatActivity?
private var fieldFragment: Fragment?

constructor(activity: A) {
fieldActivity = activity
fieldFragment = null
}

constructor(fragment: F) {
fieldFragment = fragment
fieldActivity = null
}
}


とりあえず、これで構文的なエラーは解消されました。

Genericsについてはまだまだ使い方に慣れてませんが、もう少し勉強が必要です。

何かツッコミあればご意見くださいm(_ _)m