Kotlin面向对象总结-解决多继承的问题

kotlin和Java一样只支持类的单继承。

 

1.接口实现多继承

kotlin中的接口与Java很相似,但它除了可以定义带默认实现的方法之外,还可以声明抽象的属性。

用kotlin接口实现多继承

package com.example.kotlindemo.classsdemo

interface PayKFlyer {
    fun fly()
    fun kind() = "flying animalsA"
}


interface PayKAnimal {
    val name: String
    fun eat()
    fun kind() = "flying animalsB"
}

class PayKoBird(override val name: String) : PayKFlyer, PayKAnimal {
    override fun fly() {
        println("I can fly")
    }

    override fun eat() {
        println("I can fly")
    }

    override fun kind() = super.kind()

}

fun main() {
    val bird = PayKoBird("sparrow")
    println(bird.kind())
}

flying animalsA

 

Kotlin转java

public interface PayKFlyer {
   void fly();

   @NotNull
   String kind();
   
   public static final class DefaultImpls {
      @NotNull
      public static String kind(PayKFlyer $this) {
         return "flying animalsA";
      }
   }
}


public interface PayKAnimal {
   @NotNull
   String getName();

   void eat();

   @NotNull
   String kind();

   public static final class DefaultImpls {
      @NotNull
      public static String kind(PayKAnimal $this) {
         return "flying animalsB";
      }
   }
}

public final class PayKoBird implements PayKFlyer, PayKAnimal {
   @NotNull
   private final String name;

   public void fly() {
      String var1 = "I can fly";
      System.out.println(var1);
   }

   public void eat() {
      String var1 = "I can fly";
      System.out.println(var1);
   }

   @NotNull
   public String kind() {
      return PayKFlyer.DefaultImpls.kind(this);
   }

   @NotNull
   public String getName() {
      return this.name;
   }

   public PayKoBird(@NotNull String name) {
      Intrinsics.checkParameterIsNotNull(name, "name");
      super();
      this.name = name;
   }
}

public static final void main() {
   PayKoBird bird = new PayKoBird("sparrow");
   String var1 = bird.kind();
   System.out.println(var1);
}

// $FF: synthetic method
public static void main(String[] var0) {
   main();
}

 

如果是同时存在相同的属性名:

interface PayKFlyer {
    val name: String
        get() = "ooo"

    fun fly()
    fun kind() = "flying animalsA"
}


interface PayKAnimal {
    val name: String
    fun eat()
    fun kind() = "flying animalsB"
}

class PayKoBird2() : PayKFlyer, PayKAnimal {
    override val name: String
        get()=super.name

    override fun fly() {
        println("I can fly")
    }

    override fun eat() {
        println("I can fly")
    }

    override fun kind() = super.kind()

}

 

Kotlin通过super关键字,来指定继承哪一个父接口,(super.kind()),也可以直接覆盖父接口的方法。

Kotlin实现接口的相关语法

1.在Kotlin实现一个接口时,需要实现接口中没有默认实现的方法及未初始化的属性,若同时实现多个接口,而接口之间有相同方法名的默认实现时,需要通过super关键字来

指定使用哪一个接口的方法或者直接重写这个方法。

2.如果是默认的接口方法,可以在实现类中通过"super"这种方式调用它,其中T为拥有该方法的接口名。

3.在实现接口的属性和方法时,都必须带上override关键字,不能省略。

上面代码中是通过主构造函数的方式来实现接口中的name属性的,(其实通过val声明的构造方法参数,其实是在类内部定义了一个同名属性)

class PayKoBird3(name:String):PayKFlyer, PayKAnimal {
    
    override val name:String
    
    init {
        this.name = name
    }

    override fun fly() {
        println("I can fly")
    }

    override fun kind() = super.kind()

    override fun eat() {
        println("I can fly")
    }
}

 

getter和setter注意点:

1.用val声明的属性将只有getter方法,因为它不可修改;而用var修饰的属性将同时拥有getter和setter方法。

2.用private修饰的属性编译器将会省略掉getter和setter方法,因为在外部类已经无法访问它了,这两个方法就没有存在的意义了。

 

内部类解决多继承的方案

由于内部类可以继承一个与外部无关的类,所以保证了内部类的独立性,从而可以解决多继承的问题。

在kotlin中定义一个内部类,需要inner关键字。

package com.example.kotlindemo.classsdemo

class PayOuterKotlin {

    val name = "This is truely kotlin's inner class."

    inner class PayInnerKotlin {
        fun println() {
            println("the name is $name")
        }
    }
}

 

内部类与嵌套类

在Java中,通过在内部类的语法上增加一个static关键字,把它变成嵌套类。而kotlin是默认是一个嵌套类,必须加上一个inner关键字才是一个内部类,也就是把静态的内部类看出嵌套类。

 

内部类与嵌套类(静态内部类)的区别

内部类包含着对其外部类实例的引用,在内部类中我们可以使用外部类中的属性。而嵌套类不包含对其外部类实例的引用,所以无法调用其外部类的属性。

open class PayHorse {
    fun runFast() {
        println("I can run fast")
    }
}

open class PayDonkey {
    fun doLongTimeThing() {
        println("I can do some thing long time")
    }
}

class Mule {
    fun runFast() {
        HorseC().runFast()
    }

    fun doLongTimeThing() {
        DonkeyC().doLongTimeThing()
    }

    private inner class HorseC : PayHorse()
    private inner class DonkeyC : PayDonkey()
}

 

注意点:

1.一个类内部可以定义多个内部类,每个内部类的实例都有自己独立的状态,它们与外部对象的信息相互独立。

2.通过让内部类继承不同的类,就可以让外部类同时获得内部类所继承类的状态和行为。

3.利用private修饰内部类,使得其他类不能访问内部类,具有良好的封装性。

 

使用委托替代多继承

kotlin 新引入的语法——委托。

委托是一种特殊类型,用于方法事件委托。

通过by关键字可以实现委托效果,by lazy就是利用委托实现的延迟初始化语法。

class TestLazy{
    val laziness:String by lazy{
        println("I will have a value")
        "I am a lazy-initialized string"
    }
}

fun main() {
    val testLazy = TestLazy()
    val laziness = testLazy.laziness
    println(laziness)
}

 

使用委托解决多继承问题

interface PayCanFly {
    fun fly()
}

interface PayCanEat {
    fun eat()
}

open class PayFlyer : PayCanFly {
    override fun fly() {
        println("I can fly")
    }
}

open class PayAnimal : PayCanEat {
    override fun eat() {
        println("I can eat")
    }
}


class PayAnimalBird(flyer: PayFlyer, animal: PayAnimal) : PayCanFly by flyer, PayCanEat by animal {

}


fun main() {
    val fly = PayFlyer()
    val animal = PayAnimal()
    val b = PayAnimalBird(fly, animal)
    b.fly()
    b.eat()
}

I can fly
I can eat

使用委托的优势

1.接口是无状态的,所以即使提供了默认方法实现也是较简单的,不能实现复杂的逻辑,也不推荐在接口中实现复杂的方法逻辑。可以利用委托的方式,虽然也是委托接口,

但它是用一个具体的类去实现方法逻辑,拥有更强的能力。

2.假设需要继承的类是A,委托对象是B、C,在具体调用的时候并不像组合一样A.B.method,而是可以直接调用A.method,这更能表达A拥有该method的能力,更加直观,虽然背后是通过委托对象来执行具体方法逻辑的。

 

参考kotlin核心编程

你可能感兴趣的:(Kotlin)