Kotlin核心编程(七)
多继承问题
接口实现多继承问题
interface Flyer{
fun fly()
fun kind()= "flying animals Flyer"
}
interface Animal{
val name:String
fun eat()
fun kind()="fly animals Animal"
}
class Bird(override val name: String):Flyer,Animal{
override fun fly() {
println("i am fly")
}
override fun kind() = super<Flyer>.kind() // super关键字,我们
// 可以利⽤它来指定继承哪个⽗接⼜的⽅法,
override fun eat() {
println("i am eat")
}
}
fun main(args:Array<String>){
val bird = Bird("sparrow")
println(bird.kind())
}
//=======================================
interface Flyer{
fun fly()
fun kind()= "flying animals Flyer"
}
interface Animal{
val name:String
fun eat()
fun kind()="fly animals Animal"
}
class Bird(override val name: String):Flyer,Animal{
override fun fly() {
println("i am fly")
}
override fun kind() = "a flying $name" /*。当然我们也可以主动实现⽅法,
覆盖⽗接的⽅法。*/
override fun eat() {
println("i am eat")
}
}
fun main(args:Array<String>){
val bird = Bird("sparrow")
println(bird.kind())
}
/*
1)在Kotlin中实现⼀个接⼜时,需要实现接⼜中没有默认实现的
⽅法及未初始化的属性,若同时实现多个接⼜,⽽接⼜间又有相同⽅
法名的默认实现时,则需要主动指定使⽤哪个接⼜的⽅法或者重写⽅
法;
2) 如 果 是 默 认 的 接 ⼜ ⽅ 法, 你 可 以 在 实 现 类 中 通 过
“ super<T>” 这种⽅式调⽤它,其中T为拥有该⽅法的接⼜名;
3)在实现接⼜的属性和⽅法时,都必须带上override关键字,不
能省略。*/
/*
通过val声明的构造⽅法参数,其实是在类内部定义了⼀个同名的属性,所以我们当然
还可以把name的定义放在Bird类内部
*/
class Bird(name: String):Flyer,Animal{
override val name : String // override不能少
init {
this.name=name
}
override fun fly() {
println("i am fly")
}
override fun kind() = "a flying $name" /*。当然我们也可以主动实现⽅法,
覆盖⽗接的⽅法。*/
override fun eat() {
println("i am eat")
}
}
// 也可使用get方法,初始化name
override val name: String
get() = name
getter和setter
你在声明⼀个类的属性时,要知道背后Kotlin编译器也帮你⽣成了getter和setter⽅法。当然你也可以主动声明这两个⽅法来实现⼀些特殊的逻辑。还有以下两点需要注意:
- ⽤val声明的属性将只有getter⽅法,因为它不可修改;⽽⽤var修饰的属性将同时拥有getter和setter⽅法。
- ⽤private修饰的属性编译器将会省略getter和setter⽅法,因为在类外部已经⽆法访问它了,这两个⽅法的存在也就没有意义了。
内部类解决多继承问题
class OuterKotlin{
val name = "This is not Kotlin's inner class syntax"
class ErrorInnerKotlin{ // 这是嵌套类
fun printName(){
println("this name is $name") //Unresolved reference: name
}
}
}
/*
当前我们声明的并不是Kotlin中的内部类,⽽是嵌套类的语法。
如果要在Kotlin 中声明⼀个内部类,我们必须在这个类前⾯加⼀个inner关键字,就像这样⼦:*/
class OuterKotlin{
val name = "This is not Kotlin's inner class syntax"
inner class InnerKotlin{ // 这是嵌套类
fun printName(){
println("this name is $name")
}
}
}
内部类和嵌套类
/*
* Kotlin是相反的思路,默认是⼀个嵌套类,必须加上inner关键字才是⼀个内部类,
* 也就是说可以把静态的内部类看成嵌套类。
* 内部类和嵌套类有明显的差别,具体体现在:内部类包含着对其外部类实例的引⽤,在内部类中我们可以使⽤外部类中的属性,
* ⽐如上⾯例⼦中的name属性;⽽嵌套类不包含对其外部类实例的引⽤,所以它⽆法调⽤其外部类的属性。*/
/*我们使用下面的例子来实现内部类实现多继承,主要是马和驴子生下了驴子*/
open class Horse{ //妈
fun runFast(){
println("I can run fast")
}
}
open class Donkey{ // 驴子
fun doLongTimeThing(){
println("I can do some thing long time")
}
}
class Mule{ // 骡子
fun runFast(){
HorseC().runFast()
}
fun doLongTimeThing(){
DonkeyC().doLongTimeThing()
}
private inner class HorseC:Horse()
private inner class DonkeyC:Donkey()
}
/*
1、我们可以在⼀个类内部定义多个内部类,每个内部类的实例都有⾃⼰的独⽴状态,它们与外部对象的信息相互独⽴;
2、通过让内部类HorseC、DonkeyC分别继承Horse和Donkey这两个外部类,我们就可以在Mule类中定义它们的实例对象,
从⽽获得了Horse和Donkey两者不同的状态和⾏为;
3.我们可以利⽤private修饰内部类,使得其他类都不能访问内部类,具有⾮常良好的封装性。*/
使⽤委托代替多继承
/*简单来说,委托是⼀种特殊的类型,⽤于⽅法事件委托,
⽐如你调⽤A类的methodA⽅法,其实背后是B类的methodA去执⾏。
我们只需通过by关键字就可以实现委托的效果。⽐如我们之前提过的by lazy语法,
其实就是利⽤委托实现的延迟初始化语法。*/
interface CanFly{
fun fly()
}
interface CanEat{
fun eat()
}
open class Flyer1 : CanFly{
override fun fly() {
println("I can fly")
}
}
open class Animal1 : CanEat{
override fun eat() {
println("I can eat")
}
}
class Bird1(fly:Flyer1,animal: Animal1):CanFly by fly,CanEat by animal{}
fun main(){
val fly = Flyer1()
val animal = Animal1()
val b = Bird1(fly,animal)
b.fly()
b.eat()
}
/*
* 1、前⾯说到接⼜是⽆状态的,所以即使它提供了默认⽅法实现也是很简单的,不能实现复杂的逻辑,
* 也不推荐在接⼜中实现复杂的⽅法逻辑。我们可以利⽤上⾯委托的这种⽅式,虽然它也是接⼜委托,
* 但它是⽤⼀个具体的类去实现⽅法逻辑,可以拥有更强⼤的能⼒。
* 2、假设我们需要继承的类是A,委托对象是B、C、我们在具体调⽤的时候并不是像组合⼀样A.B.method,
* ⽽是可以直接调⽤A.method,这更能表达A拥有该method的能⼒,
* 更加直观,虽然背后也是通过委托对象来执⾏具体的⽅法逻辑的。*/
数据类
// 定义数据类
data class Bird2(var weight:Double, var age:Int, var color: String)
// 编译成Java代码
public final class Bird2 {
private double weight;
private int age;
@NotNull
private String color;
public final double getWeight() {
return this.weight;
}
public final void setWeight(double var1) {
this.weight = var1;
}
public final int getAge() {
return this.age;
}
public final void setAge(int var1) {
this.age = var1;
}
@NotNull
public final String getColor() {
return this.color;
}
public final void setColor(@NotNull String var1) {
Intrinsics.checkNotNullParameter(var1, "<set-?>");
this.color = var1;
}
public Bird2(double weight, int age, @NotNull String color) {
Intrinsics.checkNotNullParameter(color, "color");
super();
this.weight = weight;
this.age = age;
this.color = color;
}
public final double component1() {
return this.weight;
}
public final int component2() {
return this.age;
}
@NotNull
public final String component3() {
return this.color;
}
@NotNull
public final Bird2 copy(double weight, int age, @NotNull String color) {
Intrinsics.checkNotNullParameter(color, "color");
return new Bird2(weight, age, color);
/*
的copy⽅法的主要作⽤就是帮我们从已有的数据类对象中拷贝⼀个新的数据类对象。
在copy的执⾏过程中,若你未指定具体属性的值,
那么新⽣成的对象的属性值将使⽤被copy对象的属性值,
这便是我们平常所说的浅拷贝*/
}
// $FF: synthetic method
public static Bird2 copy$default(Bird2 var0, double var1, int var3, String var4, int var5, Object var6) {
if ((var5 & 1) != 0) {
var1 = var0.weight;
}
if ((var5 & 2) != 0) {
var3 = var0.age;
}
if ((var5 & 4) != 0) {
var4 = var0.color;
}
return var0.copy(var1, var3, var4);
}
@NotNull
public String toString() {
return "Bird2(weight=" + this.weight + ", age=" + this.age + ", color=" + this.color + ")";
}
public int hashCode() {
int var10000 = (Double.hashCode(this.weight) * 31 + Integer.hashCode(this.age)) * 31;
String var10001 = this.color;
return var10000 + (var10001 != null ? var10001.hashCode() : 0);
}
public boolean equals(@Nullable Object var1) {
if (this != var1) {
if (var1 instanceof Bird2) {
Bird2 var2 = (Bird2)var1;
if (Double.compare(this.weight, var2.weight) == 0 && this.age == var2.age && Intrinsics.areEqual(this.color, var2.color)) {
return true;
}
}
return false;
} else {
return true;
}
}
}
/*这段代码是不是和JavaBean代码很相似,同样有getter/setter、
equals、hashcode、构造函数等⽅法,其中的equals和hashcode使得
⼀个数据类对象可以像普通类型的实例⼀样进⾏判等*/
关于componentN()方法解释
Pair和Triple
// 看看他们的源码
//Pair
public data class Pair<out A, out B>(
public val first: A,
public val second: B)
//Triple
public data class Triple<out A, out B, out C>(
public val first: A,
public val second: B,
public val third: C)
// 关于使用
val pair = Pair(20.0, 1)
val triple = Triple(20.0, 1, "blue")
//利⽤属性顺序获取值
val weightP = pair.first
val ageP = pair.second
val weightT = triple.first
val ageT = triple.second
val colorT = triple.third
//当然我们也可以利⽤解构
val (weightP, ageP) = Pair(20.0, 1)
val (weightT, ageT, colorT) = Triple(20.0, 1, "blue")
数据类的约定与使⽤
如果你要在Kotlin声明⼀个数据类,必须满⾜以下⼏点条件:
·数据类必须拥有⼀个构造⽅法,该⽅法⾄少包含⼀个参数,⼀个没有数据的数据类是没有任何⽤处的;
·与普通的类不同,数据类构造⽅法的参数强制使⽤var或者val进⾏声明;
·data class之前不能⽤abstract、open、sealed或者inner进⾏修饰;
·在Kotlin1.1版本前数据类只允许实现接⼜,之后的版本既可以实现接⼜也可以继承类。