Kotlin 学習の道 (6): 継承

序文

Kotlin の継承は Java の継承と同じですが、違いは Kotlin:を使用してextends

1. クラス継承

Kotlin はこれを使用して:継承を表し、Java はそれを使用してexteds継承を表します。

// 父类Person
open class Person()

// 子类Man
class Man() : Person()

上記は、最も単純な Kotlin 継承の例です。ここで、Kotlin のクラスはデフォルトで final (クラスの継承が許可されていないことを意味します) であり、クラスが他のクラスに継承できることを示すためにキーワードの前に追加する必要があることにも注意してくださいclassopenJava と同様に、Kotlin にも単一継承があります。
Java 継承と同様に、サブクラスは親クラスのプロパティとメソッドを使用できます。

open class Person {
    
    
    var name: String = ""

    var age: Int = -1

    fun eat(){
    
    
        println("eat food!")
    }
}

class Man() : Person()

fun main() {
    
    
    val man = Man()
    man.age = 22
    man.name = "Tom"
    println("age is ${
      
      man.age}")
    println("name is ${
      
      man.name}")
    man.eat()
}

出力結果:

age is 22
name is Tom
eat food!

上記のコードからわかるように、サブクラスは親クラスのプロパティとメソッドをMan使用できます。Person

2.継承のコンストラクタ

Kotlin での継承の場合、コンストラクタの構文は何ですか? まず、Java がそれを処理する方法を見てみましょう。

2.1 継承における Java コンストラクター

Java は、super次のコードのように、親クラスのコンストラクターへのアクセスと値の受け渡しを実装します。

// Person类(父类)
public class Person {
    
    
    private String name;

    private int age;

    public Person(String name, int age) {
    
    
        this.name = name;
        this.age = age;
    }
}

// Man类(子类)
public class Man extends Person{
    
    

    public Man(String name, int age) {
    
    
        super(name, age);
    }
}

パラメーターはキーワードを介してsuper親クラスに渡すことができ、Create constructor matching superコンストラクターは AndroidStudio をクリックすることで自動的に生成できます。Kotlin がこの問題をどのように処理するかを見てみましょう。

2.2 継承における Kotlin コンストラクター

Kotlin でこの関数を実装するには、ごく少量のコードしか必要ありません。

// Person
open class Person(var name:String, var age:Int) {
    
    
    // Empty Code
}

// Man类
class Man(name: String, age: Int): Person(name, age) {
    
    
	// Empty Code
}

fun main() {
    
    
    val man = Man("Tom", 22)
    println("name is ${
      
      man.name} age is ${
      
      man.age}")
}

印刷結果:

name is Tom age is 22

パラメータは Man クラスのコンストラクタを介して渡されname親クラスに渡されますage後者を追加せずに実行可能ですか?と尋ねる人もいるかもしれません。明らかにそうではありません。クラス コンストラクター はありません。クラス コンストラクターの単なるパラメーターであり、クラスのメンバー変数ではありません。クラスが追加された場合、それは可能ですか?答えはノーなので、親クラスとサブクラスには 2 つのメンバー変数セットがあり、間違いなくエラーが報告されます。ただし、後で詳しく説明する親クラスのメンバー変数をオーバーライドできます。: Person(name, age)
: Person(name, age)
ManvarMan
Manvar

2.3 Kotlin における複数のコンストラクターの場合

Kotlin には 1 次コンストラクターと 2 次コンストラクターがありますが、親クラスに複数のコンストラクターがある場合、サブクラスはそれをどのように継承しますか?
最初の書き方:

// Person类,这里有三个构造函数
open class Person(var name: String, var age: Int) {
    
    
    constructor(name: String): this(name, -1)
    constructor(age: Int): this("", age)
}

// Man类
class Man(name: String, age: Int) : Person(name, age) {
    
    
	// Empty Code
}

親クラスが複数のコンストラクタを持つ場合も上記と同様に記述できます。ただし、これにより、オブジェクトが 2 次コンストラクターとして作成されなくなります。

fun main(){
    
    
    val man = Man("Tom") // 报错!
    println("name is ${
      
      man.name} age is ${
      
      man.age}")
}

この理由は、親クラスのプライマリ コンストラクターのみを書き換え、親クラスのセカンダリ コンストラクターを書き換えないためです。この場合、このように書くことができます。上記の例と同じで、コードは次のとおりです。

// Person类,这里有三个构造函数
open class Person(var name: String, var age: Int) {
    
    
    constructor(name: String): this(name, -1) // 次构造函数
    constructor(age: Int): this("", age) // 次构造函数
}

// Man类
class Man: Person {
    
    
    constructor(name: String, age: Int) : super(name, age) // 对应Person类的主构造函数
    constructor(name: String) : super(name) // 对应Person类的constructor(name: String)
    constructor(age: Int) : super(age) // 对应Person类的constructor(age: Int)
}

親クラスの 3 つのサブコンストラクターを直接書き換えて、super親クラスを指すコンストラクターを使用するだけです。
簡単かつ迅速に構築するために、AndroidStudio のショートカット作成方法を使用できます。
まず、クラス内でマウスの右ボタンをクリックすると、次のメニューがポップアップします。
コンストラクタのショートカット作成
ポップアップ メニューの[生成]ボタンをクリックし、次に次のメニューがポップアップします。
コンストラクタのショートカット作成

最後に、[セカンダリ コンストラクター]ボタンをクリックして、書き換える必要があるコンストラクターを選択します。
コンストラクタのショートカット作成
このショートカットを使用すると、親クラスのすべてのプライマリ コンストラクターとセカンダリ コンストラクターをすばやく書き換えることができます。

3. 書き換えとオーバーロード

この章では書き換えに焦点を当てています.オーバーロードについては、以前の記事Kotlin Learning Road (3):
1.3 Summary in Functions を参照してください。

3.1 メソッドの書き換え

コードは上記の例のままですが、親クラスにはもう 1 つのテスト メソッドがあります。

// Person
open class Person(var name:String, var age:Int) {
    
    

    open fun testMethod(){
    
     // 注意这里加上了open,表示可以被重写。默认情况下是fina
        println("父类test!")
    }
}

// Man类
class Man(name: String, age: Int): Person(name, age) {
    
    

    override fun testMethod(){
    
     // 类似Java的override重写标志
        println("子类test!")
    }
}

fun main(){
    
    
    val man = Man("Tom", 22)
    man.testMethod()
}

出力結果:

子类test!

public final ここで強調する必要があるのは、特に、修飾子を追加する前の Kotlin のメソッドは常に同じであるため、final オーバーライドする必要があるメソッドにopen修飾子を追加することです。
書き換え方法は、同じ2 つ

  • 2 つの同じ: 同じメソッド名、同じパラメーター型
// Person
open class Person(var name:String, var age:Int) {
    
    

    open fun testMethod(str: String){
    
    
        println("父类test! $str")
    }
}

// Man类
class Man(name: String, age: Int): Person(name, age) {
    
    

    override fun testMethod(str: String){
    
    
        println("子类test! $str")
    }
}

fun main(){
    
    
    val man = Man("Tom", 22)
    man.testMethod("aaa")
}
  • 2 つの小さい: サブクラスの戻り値の型が親クラスの戻り値の型 (戻り値の型の範囲) より小さいか等しい、およびサブクラスによってスローされる例外の型が親クラスのそれより小さいか等しい
// Person
open class Person(var name:String, var age:Int) {
    
    

    open fun testMethod(): Any?{
    
    
        println("父类test!")
        return null
    }
}

// Man类
class Man(name: String, age: Int): Person(name, age) {
    
    

    override fun testMethod(): String{
    
    
        println("子类test!")
        return "test"
    }
}

上記の例では、親クラスのメソッドの戻り値の型はAny?、サブクラスのメソッドの戻り値の型は となっていますがString、継承関係から考えると、String継承の範囲Any?は当然Any?より大きくなりますStringただし、これは一般的にあまり使用されません。

  • 1 つの大きな問題: サブクラスの修飾子のアクセス許可が、親クラスの修飾子のアクセス許可以上であること
// Person
open class Person(var name:String, var age:Int) {
    
    

    protected open fun testMethod(): Any?{
    
    
        println("父类test!")
        return null
    }
}

// Man类
class Man(name: String, age: Int): Person(name, age) {
    
    

    override fun testMethod(): String {
    
    // 如果这里改为private就会报错
        println("子类test!")
        return "test"
    }
}

3.2 属性の書き換え

プロパティの書き換えは、メソッドの書き換えに似ています。

// Person
open class Person(var name:String, var age:Int) {
    
    
    open var test = -1 // 和方法一样,加上了open
}

// Man类
class Man(name: String, age: Int): Person(name, age) {
    
    
    override var test = 0 // 这里加上了override标志
}

fun main(){
    
    
    val man = Man("Tom", 22)
    println("test value is ${
      
      man.test}")
}

出力結果:

test value is 0

test結果から、親クラスのメンバー変数がサブクラスによって上書きされていることがわかります。一般に、この関数はめったに使用されず、属性を書き換えるとLiskov 置換の原則が破られる可能性があります (サブクラスは親クラスの関数を拡張できますが、親クラスの元の関数を変更することはできません)。

4.スーパーキーワード

Java と同様に、Kotlin にも super キーワードがあります。使用法は Java に似ており、どちらも親クラスを参照し、使用法は Java に似ています。

4.1 簡単な使い方

// Person
open class Person(var name:String, var age:Int) {
    
    
    // Empty Code
}

// Man类
class Man(name: String, age: Int): Person(name, age) {
    
    

    fun test(){
    
    
        println("name is ${
      
      super.name}")
    }
}

fun main(){
    
    
    val man = Man("Tom", 22)
    man.test()
}

出力結果:

name is Tom

上記の例は、ズボンを脱いでおならをするようなものですが、実際には取り除くことができますsuperその主な機能は、メソッドの書き換えに引き続き使用されます。

4.2 複雑な状況での使用

ここでは、比較的複雑な状況でのsuper使用法を示します。

4.2.1 サブクラスオーバーライドメソッドでの super の使用

書き換え方法で使用する方法は、super基本的に Java と同じです。

// Person
open class Person(var name:String, var age:Int) {
    
    

    open var test = -1

    open fun testMethod(){
    
    
        println("父类test!")
    }
}

// Man类
class Man(name: String, age: Int): Person(name, age) {
    
    

    override var test = 0

    override fun testMethod() {
    
    
        super.testMethod() // 和Java的用法一样,调用父类Person的方法testMethod
        println("test value is ${
      
      super.test}") // 调用的是父类Person的test
    }
}

fun main(){
    
    
    val man = Man("Tom", 22)
    man.testMethod()
}

出力結果:

父类test!
test value is -1

4.2.2 サブクラスは親インターフェース・親クラスのメソッドを選択的に呼び出す

Kotlin クラスが親クラスと複数のインターフェースを継承する場合、super親クラスのどのメソッドが呼び出されるかを区別する必要があります。Kotlinでは、super<父类/父接口>.方法親クラス/インターフェースのメソッドを経由で呼び出す

// Australopithecus接口,写法和Java类似
interface Australopithecus{
    
    

    fun testMethod(){
    
    
        println("接口test!")
    }
}

// Person
open class Person {
    
    

    open fun testMethod(){
    
    
        println("父类test!")
    }
}

// Man类,既继承了父类Man,也实现了接口Australopithecus
class Man: Person(), Australopithecus {
    
    

    override fun testMethod() {
    
    
        super<Australopithecus>.testMethod()
        super<Person>.testMethod()
    }
}

fun main(){
    
    
    val man = Man()
    man.testMethod()
}

出力結果:

接口test!
父类test!

4.2.3 サブクラスの内部クラスが親クラスのメソッドを呼び出す

この状況は、サブクラスの内部クラスが親クラスのメソッドを呼び出すというもので、次のように記述されています。super@子类.方法

// Person
open class Person {
    
    

    open fun testMethod(){
    
    
        println("父类test!")
    }
}

// Man类
class Man: Person() {
    
    

	// 被inner修饰的内部类是非静态内部类,静态内部类显然不能访问非静态内部类的成员方法
    inner class Heart{
    
    
    
        fun testMethod(){
    
    
            super@Man.testMethod()
        }
    }
}

fun main(){
    
    
    Man().Heart().testMethod()
}

出力結果:

父类test!

実際、this内部クラスにも同様の使用法があります。

// Man类
class Man {
    
    

    fun eat(){
    
    
        println("eat!")
    }

    inner class Heart{
    
    

        fun testMethod(){
    
    
            this@Man.eat() // 调用外部类的方法,这种操作更加常见
        }
    }
}

fun main(){
    
    
    Man().Heart().testMethod()
}

出力結果:

eat!

それがサブクラスでなければならないsuperかどうかを覚えておいてください!this@

おすすめ

転載: blog.csdn.net/RQ997832/article/details/125155911
おすすめ