Kotlin汎用型パラメータ

6281 ワード

Kotlin汎用型パラメータ
汎用型では、タイプパラメータ付きのタイプを定義できます.このタイプのインスタンスが作成されると、タイプパラメータはタイプ実パラメータと呼ばれる特定のタイプに置き換えられます.例えば、リストタイプの変数がある場合、このリストにどのようなものが格納できるかを明らかにすることは意味がある.タイプパラメータは、「この変数は文字列リストを保存する」のではなく、「この変数はリストを保存する」のように正確に記述できます.Kotlin説明「文字列リスト」の構文はJavaと同じように見えます:List.クラスに複数のタイプのパラメータを宣言することもできます.たとえば、Mapクラスには、キータイプと値タイプの2つのパラメータタイプがあります.class Mapです.具体的なタイプのインスタンスを使用して、それをインスタンス化することができます:Map.現在、多くの概念はJavaと変わらない.
通常のタイプと同様に、Kotlinコンパイラは実パラメータタイプを導出することもよくあります.
val authors= listOf("Dmitry","Svetlana")

listOf関数に渡される値は文字列であるため、コンパイラはリストを作成していることを導出します.一方、空のリストを作成したい場合は、タイプの実パラメータを導出できる手がかりがなく、表示する指定があります.リストを作成するには、変数宣言で汎用を説明するタイプを選択するか、穿具リストの関数でタイプ実パラメータを説明します.
  val readers: MutableList = mutableListOf()

  val readers = mutableListOf()

2つの声明は等価である.
1.汎用関数と属性
リストを使用する関数を記述する場合は、特定のタイプの要素リストではなく、任意のリストで使用できるようにするには、汎用関数を記述する必要があります.汎用関数には独自のタイプパラメータがあります.これらのタイプパラメータは、関数呼び出しのたびに特定のタイプの実パラメータに置き換える必要があります.
ほとんどのコレクションを使用するライブラリ関数は汎用的です.
fun  List.slice(indices:IntRange):List

受信者と戻りタイプは関数タイプのパラメータTを用い,それらのタイプはいずれもリストである.この関数を特定のリストで呼び出すと、実パラメータを表示して指定できます.しかし、ほとんどの場合、コンパイラがタイプを導くので、そうする必要はありません.
汎用関数の呼び出し
val letters = ('a'..'z').toList()
LogS(letters.slice(0..2))//[a, b, c]
LogS(letters.slice(10..13))//[k, l, m, n]

この2回の呼び出しの結果はいずれもリストである.コンパイラバー関数はタイプリストのTを返し,導出したタイプCharに置き換える.
汎化された高次関数を呼び出す
 val authors = listOf("Dmitry", "Svetlana")
 val readers = mutableListOf(/*....*/)

 fun  List.filter(predicate: (T) -> Boolean): List
 readers.filter { it in authors }

この例で自動的に生成されるlambdaパラメータitのタイプはStringである.コンパイラはそれを導出しなければならない:結局、関数宣言ではlambdaパラメータは汎用タイプTである.コンパイラはTがStringであると推定し,関数がList上で呼び出されるべきであることを知っているため,その受信者readersの真のタイプはListである.
クラスまたはインタフェースのメソッド、最上位関数、および拡張関数にタイプパラメータを宣言できます.前の例では、タイプパラメータは受信者およびlambdaパラメータのタイプに使用されます.
また、同じ構文で汎用的な拡張プロパティを宣言することもできます.たとえば、リストの最後から2番目の要素の拡張プロパティを返します.
 val List.penultimate:T
      get() = this[size-2]
 LogS(listOf(1,2,3,4).parallelStream())

2.汎用クラスの宣言
Javaと同様に、Kotlinはクラス名の後に一対のカッコを付け、タイプパラメータをカッコ内に置くことで汎用クラスおよび汎用インタフェースを宣言します.宣言すると、クラスのボディ内で他のタイプのようにタイプパラメータを使用できます.標準javaインタフェースリストでKotlinを使用して宣言する方法を見てみましょう.ほとんどのメソッド定義を省き、例を簡単にしました.
  interface List{
        operator fun get(index:Int):T
    }

クラスが汎用クラスを継承している場合は、ベースタイプの汎用パラメータにタイプ実パラメータを提供する必要があります.これは、特定のタイプまたは別のタイプのパラメータです.
class StringList: MainActivity.List {
    override fun get(index: Int): String ="..."
}

class String:Comparable{
    override fun compareTo(other: String): Int =2
}

StringListタイプはString要素のみを含むように宣言されているので、Stringをベースタイプのタイプ実パラメータとして使用します.サブクラスのどの関数も正しいタイプでTを置き換えなければならないので、StringListではfun get(Int):Tではなく、関数署名get(Int):Srtingが得られます.
クラスArrayListは、自身のタイプパラメータTを定義し、親クラスのタイプ実パラメータとして指定する.ArrayListのTとListのTは異なり、同じ名前を残す必要はありません.
1つのクラスは、それ自体をタイプ実参照として参照することもできます.Comparableインタフェースを実装するクラスがこのモードの古典的な例である.比較可能な要素は、同じタイプのオブジェクトと比較する方法を定義する必要があります.
interface Comparavle{
    fun compareTo(other:T):Int
}

class String:Comparable{
    override fun compareTo(other: String): Int =/*q*/
}

StringクラスはComparable汎用インタフェースを実現し、タイプStringをタイプ実パラメータTに提供する.
これまで、汎用型とJavaではあまり差がなかったように見えます.
3.タイプパラメータ制約
タイプパラメータ制約は、(汎用)クラスおよび(汎用)関数としてのタイプ実パラメータのタイプを制限できます.リスト要素の和を計算する関数を例にとります.リストおよびリストで使用できますが、リストのようなリストでは使用できません.この制約を表すにはsumのタイプパラメータが数値でなければならないことを示すタイプパラメータ制約を定義できます.
タイプを汎用タイプパラメータの上界コンストレイントとして指定すると、汎用タイプの特定の初期化では、対応するタイプの実パラメータがこの特定のタイプまたはそのサブタイプである必要があります.
コンストレイントを定義し、タイプパラメータ名の後にコロンを置き、タイプパラメータの境界としてタイプが続くようにします.Javaでは,キーワードextendsを用いて,T sum(List list)という概念を表す.
この関数呼び出しでは、特定のタイプの実パラメータがNumberを継承しているため、許可されます.
タイプパラメータTの上界を指定すると、タイプTの値を上界(タイプ)の値として使用できます.
fun  oneHalf(value: T): Double {
    return value.toDouble() / 2.0
}
LogS(oneHalf(3))//1.5

タイプパラメータ制約付き関数の宣言
fun > max(first:T,second:T):T{
    return if (first>second) first else second
}
LogS(max("kotlin","java"))//kotlin

比較できないエントリにmaxメソッドを使用しようとすると、コードはコンパイルされません.
LogS("kotlin",42)

Tの上界は汎用型Comparableである.前述したように、StringタイプはComparableを継承し、Stringはmax関数の有効なタイプの実パラメータになります.
first>secondの略記形式はKotlinの演算子規則に従ってfirstにコンパイルされることを覚えておいてください.compareTo(second)>0.この比較が可能なのは、firstのタイプTがComparableから継承されているため、firstと別のタイプTの要素を比較することができます.
ごく少数の場合、1つのタイプのパラメータに複数のコンストレイントを指定する必要があります.この場合、少し異なる構文を使用する必要があります.
1つのタイプパラメータに複数のコンストレイントを割り当てる
   fun  ensureTrailingPeriod(seq:T)
        where T:CharSequence,T:Appendable{
        if (!seq.endsWith('.')){
            seq.append('.')
        }
    }
 val helloWorld=StringBuilder("Hello world")
 ensureTrailingPeriod(helloWorld)
 LogS(helloWorld)//Hello world.

この場合、タイプ実パラメータのタイプとしてCharSequenceとAppendableの2つのインタフェースを実装する必要があることを説明することができる.これは、このタイプの値がデータへのアクセスとデータの変更の2つの操作を使用できることを意味します.
4.型パラメータを空にしない
汎用クラスまたは汎用関数を宣言する場合は、空のタイプの実パラメータを含む任意のタイプの実パラメータをパラメータタイプの実パラメータに置き換えることができます.実際に上界を指定していないタイプのパラメータはAnyを使用しますか?このデフォルトの上界.
 class Processor{
        fun process(value:T){
            value?.hashCode()
        }
    }

プロセス関数では、Tは疑問符タグを使用していないにもかかわらず、パラメータValueは空です.次の場合、Processorクラスの特定の初期化時にTは空のタイプを使用できるためです.
al nullableStringProcess=Processor()
nullableStringProcess.process(null)

置換タイプのパラメータが常に空でないことを保証したい場合は、コンストレイントオブジェクトを指定することで実現できます.制御性以外に制限がない場合は、デフォルトのAnyの代わりにAnyを使用できますか?上界として:
 class Processor{
        fun process(value:T){
            value?.hashCode()
        }
    }

コンストレイントは、タイプTが常に空でないタイプであることを保証します.コンパイラはコードProcessorを受信しません.タイプの実パラメータStringのためです.Anyのサブタイプではありません.
注意:タイプAnyだけでなく、任意の非空タイプを上界として指定することで、タイプパラメータを非空にすることができます.
これまで、Javaに最も近いテーマである汎用的な基礎概念を紹介してきました.