Kotlin面向对象总结-访问控制原则

在Java中,如果希望一个类被别人继承或者修改,那么可以使用 final来修饰它。可以用 public、private、protected等修饰符来描述一个类、方法或属性的可见性。

Kotlin在默认修饰符的设计上采用了与Java不同的思路。Kotlin相比Java,对一个类、方法或属性有着不一样的访问控制原则。

限制修饰符

指定一个类、方法或属性的修改或者重写权限时,需要用到限制修饰符。

继承是面向对象的基本特征之一,比较灵活。

open class KFlyBird {

    open fun fly() {
        println("I can fly.")
    }
}

class Penguin: KFlyBird() {

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

可以发现Kotlin与Java中不同的地方

1.Kotlin中没有采用Java中的extends和implements关键词,而是使用":"来代替类的继承和接口实现。

2.由于 Kotlin中类和方法默认是不可被继承或重写的,所以必须加上open修饰符。

开发注意点:

子类应该尽量避免重写父类中的非抽象方法,因为一旦父类变更了方法,子类的方法调用很可能会出错,而且重写父类非抽象方法违背了面向对象设计原则中的“里氏替换原则”。

什么是里氏替换原则?

通俗的理解:子类可以扩展父类的功能,但不能改变父类原有的功能。包含以下四个原则:

1.子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。

2.子类可以增加自己特有的方法。

3.当子类的方法实现父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。

4.当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。

1.类的默认修饰符:final

kotlin中的类或方法默认是不允许被继承或重写的。

class PayMyBird {
    val weight: Double = 500.0
    val color: String = "blue"
    val age: Int = 1
    fun fly() {}
}

转换成java

public final class PayMyBird {
   private final double weight = 500.0D;
   @NotNull
   private final String color = "blue";
   private final int age = 1;

   public final double getWeight() {
      return this.weight;
   }

   @NotNull
   public final String getColor() {
      return this.color;
   }

   public final int getAge() {
      return this.age;
   }

   public final void fly() {
   }
}

转化成java后可以发现,类、方法及属性前面多了一个final修饰符,由final修饰的内容将不允许被继承或者修改。String就是final修饰的,它不可被继承。

在Java中,类默认是可以被继承的,除非主动加上final修饰。而kotlin中类默认是不可继承的,除非加上open修饰符。

open class PayMyBird {
    val weight: Double = 500.0
    val color: String = "blue"
    val age: Int = 1
    fun fly() {}
}

public class PayMyBird {
   private final double weight = 500.0D;
   @NotNull
   private final String color = "blue";
   private final int age = 1;

   public final double getWeight() {
      return this.weight;
   }

   @NotNull
   public final String getColor() {
      return this.color;
   }

   public final int getAge() {
      return this.age;
   }

   public final void fly() {
   }
}

类默认final可能存在的问题

在对第三方kotlin库进行扩展时会有问题,kotlin类库肯定会比Java类库更倾向于不开放一个类的继承,Kotlin默认final可能会阻扰我们对这些类库的类进行继承,然后扩展功能。

类默认final的一些优点:

1.如果一个类默认是final,我们需要扩展它的时候,即使没有标记为open,编译器也会提醒我们。

2.kotlin 非常类似于Java,然而它对一个类库扩展的手段更加丰富。典型的案例是Android的Kotlin扩展库android-ktx。Google官方主要通过Kotlin中的扩展语法对Android标准库进行了扩展,而不是通过继承原始类的手段。在Kotlin中由于这种增强的多态性支持,类默认为final也许可以督促我们思考更正确的扩展手段。

Kotlin还可以通过密封类的语法来限制一个类的继承。

Kotlin通过sealed关键字来修饰一个类为密封类,若要继承则需要将子类定义在同一个文件中,其他文件中的类将无法继承它。但这种方式有局限性,即它不能被初始化,因为它背后是基于一个抽象类实现的。

sealed class PaySealedKBrid {
    open fun fly() = "I can fly"
}

class Eagle : PaySealedKBrid()

基于抽象类实现:

public abstract class PaySealedKBrid {
   @NotNull
   public String fly() {
      return "I can fly";
   }

   private PaySealedKBrid() {
   }

   // $FF: synthetic method
   public PaySealedKBrid(DefaultConstructorMarker $constructor_marker) {
      this();
   }
}


public final class Eagle extends PaySealedKBrid {
   public Eagle() {
      super((DefaultConstructorMarker)null);
   }
}

不能被初始化

sealed class PaySealedKBrid {
    open fun fly() = "I can fly"
}
fun main() {
    val paySealedKBrid = PaySealedKBrid()// 在同一个文件中,也是不能被初始化的
}

Kotlin中的abstract和Java中完全一样,它修饰在类前面说明这个类是抽象类,修饰在方法前面说明这是一个抽象方法。

修饰符

含义

与Java比较

open

允许被继承或重写

相当于java类与方法的默认情况

abstract

抽象类或者抽象方法

与Java一致

final

不允许继承或者重写(默认情况)

与Java主动指定final的效果一直

可见性修饰符

通过可见性修饰符指定类、方法以及属性的可见性。Kotlin中可见性修饰符注意点:

1.Kotlin与Java的默认修饰符不同,Kotlin中是public,而Java 中是default。

2.Kotlin中有一个独特的修饰符internal。

3.Kotlin可以在一个文件内单独声明方法及常量,同样支持可见性修饰符。

4.Java中除了内部类可以用 private 修饰以外,其他类都不允许private修饰,而Kotlin可以。

在 Java中我们很自然地会给类加上public修饰符,因为大多数类都可能需要在全局访问。而Java的默认修饰符是default,它只允许包内访问。而kotlin将可见性修饰符默认指定为public,而不需要显式声明。

kotlin中修饰符internal,和java中的default有点像但也有区别。internal在kotlin中的作用域可以称作“模块内访问”。

以下几种情况可以算作一个模块:

1.一个Eclipse项目

2.一个Intellij IDEA项目

3.一个Maven项目

4.一个Grandle项目

5.一组由一次Ant任务执行编译的代码

一个模块可以看作一起编译的Kotlin文件组成的集合。

Java的包内访问存在的问题:假如你在Java项目中定义了一个类,使用了默认修饰符,那么现在这个类是包私有,其他地方将无法访问它。然后,你把它打包成一个类库,并提供给其他项目使用,这时候如果有一个开发者想使用这个类,除了copy源码以外,还有一个方式是在程序中创建一个与该类相同名字的包,那么这个包下面的其他类就可以直接使用我们前面的定义的类。

Kotlin通过 internal实现模块内可见,模块内可见指的是该类只对一起编译的其他Kotlin文件可见。开发工程与第三方类库不属于同一个模块,这时候如果还想使用该类的话只有复制源码一种方式了。

在Java中很少用 private修饰类,因为Java中的类或者方法没有单独属于某个文件的概念。比如创建一个.java文件,那么它里面的类要么是public,要么是包私有,而没有只属于这个文件的概念。若要使用private修饰,那么这个只能是其他类的内部类。而kotlin中则可以用private给单独的类修饰,它的作用域就是当前这个kotlin文件。

package com.example.kotlindemo.classsdemo

class PayBMWCar(val name: String) {
    private val bMWEngine = PayEngine("BMW")
    fun getEngine(): String {
        return bMWEngine.engineType()
    }

}

private class PayEngine(val type: String) {
    fun engineType(): String {
        return "the engine type is $type"
    }
}

除了private修饰符的差别,kotlin中的protected修饰符也与Java有所不同。Java中的 protected修饰的内容作用域是包内、类及子类可访问。而在kotlin中,由于没有包作用域的概念,所以protected修饰符在kotlin中的作用域只有类及其子类。

package com.example.kotlindemo.classsdemo

private open class PayPEngine(val type: String) {
    protected open fun engineType(): String {
        return "the engine type is $type"
    }
}


class PayPBMWCar(val name: String) {
    private val bMWEngine = PayPEngine("BMW")
    fun getEngine(): String {
        return bMWEngine.engineType() //这里会报错,因为是访问不了的
    }
}

private class PayBZEngine(type: String) : PayPEngine(type) {
    override fun engineType(): String {
        return super.engineType()
    }
}

修饰符

含义

与Java比较

public

kotlin中默认修饰符,全局可见

与Java中public效果相同

protected

受保护修饰符,类及子类可见

含义一致,但作用域除了类及子类外,包内也可见。

private

私有修饰符,类内部修饰只有本类可见,类外修饰文件内可见

私有修饰符,只有类内可见

internal

模块内可见

参考:Kotlin核心编程

发布了179 篇原创文章 · 获赞 175 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/zhangying1994/article/details/104188532