第三章 面向对象
更简洁地构造类的对象
在Java中,通过重载构造函数,来实现任意参数组合来创建对象,但是这样需要实现的构造方法就会很多。 koltin的解决方法: - 构造函数默认参数;(如果这样做,在创建对象的时候,最好指定参数的名称)。
class Bird(val weight:Double = 0.00,val age:Int = 0,val color:String ="blue")
val bird1 = Bird(color = "black")
val bird1 = Bird(1000.0,"blue",1)
val bird2 = Bird(color = "red",age = 5)
- init语句块 : 它属于上述构造方法的一部分,两者在表现的形式上是分离的。如果需要在构造初始化时进行其他的额外操作就可以使用init语句块。构造方法还可以拥有多个init,它们会在对象被创建时,按照类中从上到下的顺序先后执行。
延迟初始化
- by lazy :语法特点:该变量必须是引用不可变的(即使用val声明);在被首次调用时,才会进行赋值操作。一旦赋值,后续它将不能被更改。 lazy 的背后是接受一个lambda并返回一个Lazy
实例的函数,第一次访问该属性时,会执行lazy对应的Lambda表达式并记录结果。后续访问时就返回该记录。 系统会给lazy 属性默认加上同步锁,也就是LazyThreadSafetyMode.SYNCHRONIZED ,它在同一时刻只允许一个线程对lazy属性进行初始化,因此它是线程安全的。 val sex by lazy(LazyThreadSafetyMode.PUBLICATION) { //并行模式 if(color == "yellow") "male" else "female" } val sex by lazy(LazyThreadSafetyMode.NONE) { //不做任何线程保证也不会有任何线程开销 if(color == "yellow") "male" else "female" }
- lateinit : 主要用于var 声明的变量,然而他不能用于基本数据类型,如Int,Long等,需要使用Integer这种包装类进行替代。
主从构造方法:
- 通过constructor 方法定义了一个新的构造方法,它被称为从构造方法。如果主构造方法存在注解或可见性修饰符,也必须像从构造方法一样加上constructor 关键字。 每一个从构造方法有两部分组成,一部分是对其他构造方法的委托,另一部分是由花括号包裹的代码块。执行顺序上会先执行委托的方法,然后执行自身代码块的逻辑。 通过this关键字来调用要委托的构造方法。如果一个类存在主构造方法,那么每一个从构造方法都要直接或间接地委托给它。
- by lazy :语法特点:该变量必须是引用不可变的(即使用val声明);在被首次调用时,才会进行赋值操作。一旦赋值,后续它将不能被更改。 lazy 的背后是接受一个lambda并返回一个Lazy
不同的访问控制原则
Kotlin中使用冒号":"来进行类的继承和接口的实现。 Kotlin中的类和方法默认是不可继承或重写的,有需要的话要加上open 修饰符。 子类应该尽量避免重写父类的非抽象方法,一旦父类变更方法,子类就方法调用就可能会出错。重写父类的非抽象方法违背了面向对象设计原则中的里氏代换原则。
什么是里氏替换原则?
通俗的理解:子类可以扩展父类的功能,但是不能改变父类原有功能。 包含了4个设计原则: - 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法; - 子类可以增加实现自己特有的方法; - 当子类的方法实现父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。 - 当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。
可见性修饰符
- Kotlin 与 Java 的默认修饰符不同,Kotlin是 public ,而Java 中是 default。
- Kotlin 中有一个独特的修饰符internal。
- Koltin 可以在一个文件内 单独声明方法及常量,同样支持可见性修饰符。
- Java中出了内部类 可以用private 修饰以外,其他类都不允许private 修饰,而Kotlin可以。
- Koltin和Java中的protected 的访问范围不同,Java中是包、类、及子类可访问,而Kotlin只允许类及子类。 internal 在Kotlin中的作用域可以被称为 模块内访问 。什么算是一个模块呢?一个Eclipse项目,一个Intellij IDEA项目、一个Maven项目,一个Gradle项目,一组由一次Ant任务执行编译的代码
Kotlin 中的 private 修饰的类,表示它的作用域就是当前这个Kotlin文件。
在Koltin实现一个接口时: - 需要实现接口中没有默认实现的方法及未初始化的属性,若同时实现多个接口,而接口间又有相同方法名的默认实现时,需要主动指定使用那个接口方法或重写方法; - 如果是默认的接口方法,需要在实现类中通过"super
Koltin在我们声明一个属性的时候,会帮助生成getter 和 setter 方法。 - 用val 声明的属性将只有getter 方法,因为它不可修改,而用var 修饰的属性将同时拥有getter 和 setter 方法。 - 用private 修饰的属性,编译器将会省略 getter 和 setter 方法,没有必要饿了,因为类外部无法访问。
如果我们要在Koltin中使用内部类,需要在类的前面加一个inner关键字。因为Koltin直接写在类中的类为嵌套类。这里的嵌套类可以看成Java中的静态内部类。
在某些场合下,内部类确实是一种解决多继承非常好的思路。
open class Horse{
fun runFast(){
println("I can run fast")
}
}
open class Donkey{
fun doLongTimeThing(){
println("I can do thing long time")
}
}
class Mule {
fun runFast(){
HorseC().runFast()
}
fun doLongTimeThing(){
DonkeyC().doLongTimeThing()
}
private inner class HorseC:Horse()
private inner class DonkeyC:Donkey()
}
使用委托代替多继承
委托是一种特殊的类型,用于方法事件委托,调用A类的methodA 方法,其实背后是B类的methodA 去执行。 在Koltin中,只需通过by关键字就可以实现委托的效果。
interface CanFly{
fun fly()
}
interface CanEat{
fun eat()
}
open class Flyer:CanFly{
override fun fly() {
println("I can fly")
}
}
open class Animal:CanEat{
override fun eat() {
println("I can eat")
}
}
class Bird(flyer: Flyer,animal: Animal):CanFly by flyer,CanEat by animal{}
fun main() {
val flyer = Flyer()
val animal =Animal()
val bird1 = Bird(flyer,animal)
bird1.eat()
bird1.fly()
}
用data class 创建数据类。
从static 到 object
在Kotlin中,引入了全新的关键字object,可以完美的代替使用static 的所有场景。object 还可以实现更多功能,比如单例对象及简化匿名表达式。
伴生对象(companion object):
"伴生"是相对于一个类而言,意为伴随某个类的对象,它属于这个类所有,因此伴生对象跟Java中static 修饰效果性质一样,全局只有一个单例,他需要声明在类的内部,在类被装载时会被初始化。 伴生对象也是实现工厂方法模式的另一种思路。
class Prize constructor(val name:String,val count:Int,val type:Int) {
companion object{
val TYPE_COMMON = 1
val TYPE_REDPACK = 1
val TYPE_COUPON = 1
private val defaultCommonPrize = Prize("普通奖品",10,Prize.TYPE_COMMON)
fun newRedPackPrize(name:String,count: Int) = Prize(name,count,Prize.TYPE_REDPACK)
fun newCouponPrize(name: String,count: Int) = Prize(name,count,Prize.TYPE_COUPON)
fun defaultCommonPrize() = defaultCommonPrize
}
}
fun main() {
val redpackPrize =Prize.newRedPackPrize("红包",10)
val couponPrize = Prize.newCouponPrize("优惠卷",10)
val commonPrize = Prize.defaultCommonPrize()
}
object 单例
单例模式最大的特点就是在系统中只能存在一个实例对象,所以在Java中我们必须通过设置构造方法私有化,以及提供静态方法创建实例的方式来创建单例对象。但是在Kotlin中可以直接使用object 来实现单例。
object 表达式
用于代替匿名内部类的object 表达式,在运行中不像之前的单例模式中说的那样,全局只存在一个对象,而是每一次运行时都会生成一个新的对象。
object表达式和Lambda表达式哪个更适合代替匿名类
当匿名内部类使用的类接口只需要实现一个方法时,使用Lambda 表达式更适合;当匿名内部类内有多个方法实现的时候,使用object 表达式更加合适。