Kotlin特集「十」:インタフェースと関数インタフェース(Functional(SAM)interfaces)

22218 ワード

前言:両親があなたを必要とする時、涙を除いて、何もありません;子供があなたを必要としないでください.耻ずかしい以外に何もありません.自分が過去を振り返る時、無駄にする以外に何もない.
一、概説
  Kotlinのインタフェースは、抽象的なメソッドの宣言を含んでもよいし、メソッド実装を含んでもよい.インタフェースと抽象クラスの違いは、インタフェースがステータスを格納できないことです.これらは属性を持つことができますが、これらの属性は抽象的であるか、アクセサ実装を提供する必要があります.同時にKotlin 1.4バージョンからインタフェースのSAM変換をサポートしています.
KotlinのインタフェースはJava 8と同様で、interfaceキーワードを使用してインタフェースを定義し、デフォルトの実装を許可します.
    interface MyInterface {
        fun share() //   ,   abstract   
        fun invite() {//   ,   abstract   
            println("invite()")//     
        }
    }

インタフェースでインプリメンテーションが提供されていない方法のデフォルトはabstract修飾であり、インプリメンテーションが提供されるのはabstract修飾ではない.サブクラスは抽象メンバーを書き換える必要があります.
二、インタフェースの使用
2.1実装インタフェース
1つのクラスまたはオブジェクトは、1つまたは複数のインタフェースを実装することができる.KotlinではJavaのようにimplementsのキーワード実装を提供するのではなく,コロン:で表され,実装が必要なインタフェース名が後に続く.
    class Student : MyInterface {
        override fun share() {//     abstract    ,        ,  invite()      ,       
            //TODO
            println("share()")
        }
    }

	//  
	var student = Student()
    student.share()
    student.invite()

印刷データは次のとおりです.
share()
invite()
abstract修飾の方法はサブクラスで書き直さなければなりません.もちろん、abstract修飾の方法はありません.書き換えて自分の実現を提供することもできます.
2.2インタフェースのプロパティ
インタフェースでプロパティを宣言したり、インタフェースで宣言したプロパティを抽象的にしたり、アクセサに実装したりすることができます.インタフェースで宣言されたプロパティには、バックアップフィールドfieldは使用できません.したがって、インタフェースで宣言されたアクセサは、バックアップフィールドを参照できません.
    interface MyInterface {
        abstract val name: String //   abstract   
        val type: Int 
            //   get   ,          field,   abstract   
            get() = 10

//        var age: Int  //  ,             field
//            get() = field

//        var sex: String = "  "//             
    }

    class Student : MyInterface {
        override val name: String //abstract         
            get() = "Android"
    }

インタフェースのプロパティのデフォルトは抽象的で、初期化値は許可されず、インタフェースはプロパティ値を保存しません.インタフェースを実装するときはプロパティを書き換える必要があります.
2.3インタフェースの継承
Kotlinのインタフェースは継承可能で、Javaと似ています.メンバーにインプリメンテーションを提供したり、新しいメンバー(関数とプロパティ)を宣言したりできます.したがって、このようなインタフェースを実装するクラスは、実装されていないメンバーを書き換えるだけでよい.
    //    
    interface BaseInterface {
        val name: String //      ,    

        fun send1() //      ,    

        fun send2() {//      ,      
            println("send2()")
        }
    }

    //    :   BaseInterface,      name
    interface ChildInterface : BaseInterface {
        val childName: String //      ,    

        override val name: String //   BaseInterface      name   
            get() = "Android"
    }

    //     :    ChildInterface   
    class People : ChildInterface {
        override val childName: String //   ChildInterface       
            get() = "Kotlin"

        override fun send1() {//   BaseInterface       
            println("send1()")
        }
    }

クラスPeopleではchildNameを実現しなければならないが、nameを実現していないのはなぜか.
ChildInterfaceインタフェースはnameを書き換えたため、属性nameは抽象的ではなく、クラスPeopleでは実現する必要はありません.childNameはChildInterfaceで実装を提供していないが抽象的であるため、Peopleでは書き換え、実装を提供しなければならない.
では、なぜクラスPeopleではsend1()を実現しなければならず、send2()を実現しなかったのでしょうか.
同様に、send1()はBaseInterfaceで実装を提供していないため、ChildInterfaceでも実装を提供していないため、抽象的であり、すべてPeopleで書き換え、実装を提供しなければならない.send2()はBaseInterfaceで実装を提供しており、抽象的ではなく、書き直さなければならないものではありません.
なぜChildInterfaceはsend1()を実現せずにクラスPeopleで実現しなければならないのですか?
ChildInterface自体がインタフェースであるため、abstractのメンバーを提供することができ、send1()は抽象的であり、Peopleは特定の実装クラスであり、Peopleが抽象クラスでない限り、親インタフェースで実装されていない方法と属性を実装しなければならない.
総じて、実装クラスは、実装されていない関数と属性を書き換える必要があります.
2.4インタフェース書き換えの競合
インタフェース書き換え競合とは,前述したクラス継承メソッドの競合と似ている.1つのサブクラスで複数の親インタフェースが実装され、異なる親インタフェースには同じメソッド名があり、サブクラスの実装時に競合し、呼び出された親の実装を確認できません.
    interface A {
        fun foo() {//      ,        
            println("A:foo()")
        }

        fun bar()//      ,       
    }

    interface B {
        fun foo() {//      ,        
            println("B:foo()")
        }

        fun bar() {//      ,        
            println("B:bar()")
        }
    }

    //   ,  A  
    class C : A {
        override fun bar() {//A   bar()     ,     ,    ,     
            println("C:bar()")
        }
    }

    //   ,     A, B     
    class D : A, B {
        //     bar(),   A    bar()     
        override fun bar() {
            super<B>.bar() //        B    bar(),   A   bar()    ,     ,     
        }

        //     A   B    foo()           ,          D      ,         A    foo()      B    foo()   ,   Kotlin       
        override fun foo() {
            super<A>.foo()//   A    foo()
            super<B>.foo()//   B    foo()
        }
    }

インタフェースA、Bは、関数foo()およびbar()を宣言し、いずれもfoo()がデフォルトで実装されているが、インタフェースBのbar()のみがデフォルトで実装されており、インタフェースAのbar()はデフォルトで実装されておらず、デフォルトでは抽象的である.インタフェースAで特定のインプリメンテーションクラスCを派生させる場合、bar()を書き換え、インプリメンテーションを提供する必要がある.上のclass Cのようです.
しかし、インタフェースAおよびBから特定の実装クラスDが派生する場合、複数のインタフェース継承のすべての方法を実装し、クラスDがどのように正確に実装されるべきかを指定する必要がある.同じ方法が複数ある場合、superを使用して親クラスのインプリメンテーションを選択的に呼び出す方法を書き換える必要があります.上のclass Dのようです.
インタフェースAおよびBのfoo()メソッドはいずれもデフォルト実装を提供しているが、名前は同じサブクラスDに競合をもたらし、呼び出しがAのfoo()実装であるかBのfoo()実装であるかを確認できないため、Kotlinはサブクラスにfoo()の書き換えを強制する.Aのbar()はデフォルトのインプリメンテーションを提供していないため、強制的に書き換える必要がある.
	//  
	val d = D()
    d.foo()
    d.bar()
    
    val c = C()
    c.foo()

印刷データは次のとおりです.
A:foo()
B:foo()
B:bar()
C:bar()

三、関数インタフェース
1つの抽象メソッドのみのインタフェースを関数インタフェースまたは単一の抽象メソッド(SAM)インタフェースと呼ぶ.関数インタフェースには、いくつかの非抽象メンバーがありますが、1つの抽象メンバーのみが使用できます.
Kotlinで関数インタフェースを宣言するには、fun修飾子を使用してインタフェースを修飾します.
    //      fun       
    fun interface KRunnable {
        fun invoke()
    }

3.1 SAM変換
Kotlin 1.4リリースでは、関数インタフェースでSAM変換を使用してLambda式を使用することで、コードをより簡潔に読み取ることができます.
「SAM変換」とは何ですか?
SAM変換、すなわちSingle Abstract Method Conversionsは、単一の非デフォルト抽象メソッドインタフェースのみに対する変換であり、条件を満たすインタフェースについてはSAM Typeと呼ばれ、KotlinではLambda式を直接使用して表すことができ、Lambdaが表す関数クラスがインタフェースのメソッドと一致することを前提としている.
関数インタフェースを実装するクラスを手動で作成するのではなく、Lambda式を使用できます.SAM変換により、Kotlinは、インタフェースの単一の方法の署名と一致する任意のLambda式を、インタフェースを実装するクラスのインスタンスに変換することができる.
たとえば、次の関数インタフェースを示します.
    fun interface IntAction {
        fun check(int: Int): Boolean
    }

SAM変換前のこのインタフェースの実現方法:
	//            
    var isEvent = object : IntAction {
        override fun check(int: Int): Boolean {
            return int % 2 == 0
        }
    }

KotlinのSAM変換を利用することで、以下の等価コードを書くことができます.
var isEvent = IntAction { it % 2 == 0 }

単純なLambda式は、多くの不要なコードを簡略化します.
	//    
    fun interface IntAction {
        fun check(int: Int): Boolean
    }
	//Lambda          
	var isEvent2 = IntAction { it % 2 == 0 }

	fun main() {
		println("8      2   :${isEvent.check(8)}")
	}

印刷データは次のとおりです.
8      2true

もちろん、Javaインタフェースに対してSAM変換を使用することもできます.
3.2関数インタフェースとタイプ別名
関数インタフェースとタイプ別名は、異なる目的で使用されます.タイプ別名は既存のタイプの名前にすぎません.新しいタイプは作成されませんが、関数インタフェースは作成できます.
タイプ別名には1つのメンバーしかありませんが、関数インタフェースには複数の非抽象メンバーと1つの抽象メンバーがあります.関数インタフェースは、他のインタフェースを実装および拡張することもできます.
以上のように、関数インタフェースはタイプ別名よりも柔軟であり、提供される機能もより多い.
これで、本文は終わります!
ソースアドレス:https://github.com/FollowExcellence/KotlinDemo-masterオリジナルの著作権を尊重してください.転載は出典を明記してください.https://blog.csdn.net/m0_37796683/article/details/107964620ありがとう!