本章节来介绍Kotlin面向对象的高级部分:
一. 扩展:kotlin的扩展是一个很独特的功能,java本身支持扩展,Kotlin为了让扩展能在JVM平台上运行,必须做了一些独特的处理。
1.扩展方法:
扩展方法的语法很简单,其实就是定义一个函数,只是函数名不要写成简单的函数,而是要在函数名前添加被扩展的类(或接口)名和点号(.)。例如:
如上:程序为Raw类扩展了info()方法之后,就像为Raw类增加了info()方法一样,所有的Raw对象都可调用info()方法。不仅如此,Raw类的子类的实例也可调用info方法。
当然,扩展也可以为Kotlin系统提供类的增加方法。例如:
切记:用为指定了Int的泛型,所以只能对该泛型为Int的list集合可以调用。如果想对所有类型的泛型可以调用的话,可以不指定明确的类型。(具体泛型的使用会在接下来的泛型篇章里细讲)
实际上,Kotlin的扩展并没有真正地修改所有的扩展的类,被扩展的类还是原来的类,没有任何改变。Kotlin扩展的本质就是定义了一个函数,当程序用对象调用扩展方法时,Kotlin在编译时会执行静态解析--就是根据调用对象,方法名找到扩展函数,转换为函数调用。
介绍一下子类和父类同时有一个扩展名一样扩展,调用的时候是如何执行的,如下:
如果getName是重写的方法,执行返回的一定是son,但是因为扩展执行的是静态解析(有编译时候的类型决定,编译的类型是Base),所以调用的是Base类的getName方法,返回的是base。
此外还有一点需要注意的是,成员的方法的优先级高于扩展的方法,所有如果成员方法和扩展方法具有同一个名字的时候,那么系统总是会执行成员方法,而不会执行扩展方法。(当然参数不同的话,就无所谓了)
也可以为可空类型扩展方法:如下:
Any这个关键字我忘记了在前面介绍没有,所在还是提上一句。 Any就是java的object,在kotlin是一切类型的父类。
2.扩展属性
Kotlin也允许扩展属性,但由于Kotlin的扩展并不是真正修改目标类,因此Koltin扩展的属性其实是通过get和set方法实现的,没有幕后字段。简单的来说,扩展的属性只能是计算属性!
kotlin的扩展属性的如下要求:
1.扩展的属性不能有初始化值(因为没有存储属性值的幕后字段)
2.不能用field关键字显示幕后字段
3.扩展只读属性必须提供getter方法,扩展读写属性必须提供getter和setter方法。
匿名扩展函数:与原来的区别在于. + 方法名,将方法名移动到前面:如下:
对于扩展在举一个最后的例子。如下:
3.const 宏的介绍
Kotlin与java的一个重大区别是:Kotlin的final修饰符不能修饰局部变量,因此open自然也不能修饰局部变量。所以Kotlin提供了宏的概念,Kotlin提供了const用来修饰可执行“宏替换”的常量。定于如下:
const的使用规则如下:
1.位于顶层或者是对象表达式的成员
2.初始化为基本类型值或者字符串类型
3.没有自定义的getter方法。
定义宏变量的时候需要注意的是:
MAX_VALUE_ONE这样的写法可以,能够得到确切的值,但是MAX_VALUE_TWO这样的写法是错误的,在编译的时候无法得到确切的值。
4.final和open
kotlin 有一个非常特别的设计:它会为非抽象类自动添加final修饰符,也会为非抽象方法,非抽象属性等无须重写的成员自动添加final修饰符。如果开发者希望取消kotlin自动添加final修饰符,可以使用open修饰符,open修饰符与final是反义词。(对于这样的设计,我个人是比较青睐的。因为一个类加上final和不加上final对于性能来说其实差距还是蛮大的,不知道原因的同学可以查阅一下。但是对于好多开发者来说,不会注意这个类加不加final,即使这个类不需要继承。所以说kotlin其实对java和java开发者来说,真的就是一种优化代码而已,方便开发高性能的代码)。
二.类的种类:
1.不可变类:不可变类的意思是创建该类的实例后,该实例的属性值是不可以改变的。其实很简单,就是对于参数加上val修饰。如下:
与可变类相比,不可变类的实例在整个生命周期中永远处于初始化状态,他的属性值不可以改变。
下面有这样一个例子:
通过这样仍然可以对address里边的属性进行改变,在语义上这样是不对的,那么如何实现彻彻底底的不可变类呢。如下:
这样写就能彻彻底底实现不可变类啦~
密封类:密封类是一种特殊的抽象类,专门用于派生子类。密封类与普通类的区别在于:密封类的子类是固定。密封类的子类必须与密封类本身在同一个文件中,在其他文件中则不能为密封类派生子类,这样就限制了在其他文件中派生子类。
上边定义了一个密封类,接下来即可在该密封类中定义抽象方法,由此可见,密封类的本质就是抽象类。有规则可以知道,密封类的所有构造器都必须是private的,不管开发者是否使用private修饰,系统都会为之自动添加private修饰。
有人会问到:那些密封类的好处是什么呢?因为密封类的子类是固定的,编译器可以清楚地知道密封类有多少个子类,所以在使用when表达式的时候,编译器可以知道是否覆盖了所有情况,从而判断是否需要添加else子句。
嵌套类和内部类:
Kotlin的嵌套类相当于java的静态内部类。而kotlin的内部类相当于java的非静态内部类。(在kotlin使用内部类的时候,用到了inner这个修饰符 inner class className {})
匿名内部类:
Java有一个非常实用的功能:匿名内部类,Kotlin则彻底抛弃了这个功能。不过同学不必担心,Kotlin提供了一个更加强大的语法:对象表达式。
对象表达式和对象声明:
对象表达式还有如下规则:
1.对象表达式不能是抽象类,因为系统在创建对象表达式时会立即创建对象。因此不允许将对象表达式定义抽象类。
2.对象表达式不能定义构造器。但对象表达式可以定义初始化块,可以通过初始化块来完成构造器需要完成的事情。
3.对象表达式可以包含内部类(有inner修饰的内部类),不能包含嵌套类。
指定零个父类型的对象表达式:
当然也可以指定多个父类型的对象表达式:
对象表达式用来代替java的匿名内部类的。那么对象声明就可以实现单例啦~什么是对象声明呢?如下:
这个就是对象声明,和对象表达式很相近。他们之前的不同在于对象声明不能定义在函数或者方法里边。
伴生对象:
在谈到对象声明的时候,还不得不说另外一个概念就是伴生对象:在类中定义的对象声明,可以用companion修饰,这样该对象就变成了伴生对象。每个类最多只能定义一个伴生对象,伴生对象相当于外部类的对象,程序可通过外部类直接访问:那么我们就来实现kotlin的单例吧~如下:
by修饰符这个是kotlin的委托,我会在接下来篇章里详细介绍。companion object {} 就完完整整的实现了单例。因为kotlin不在用static修饰符了。。那么可以用伴生对象来定义静态的常量。
如果细读的同学可以会问:你刚刚说的是伴生的修饰符 + 对象声明来实现的那么应该怎么写吧,怎么少了名字呢?
其实名字完全可以省略的,因为在调用的过程中,是当前的类名.instance, 调用变量的话是类名.DB_NAME等等,所以起不起名字都没有关系。
伴生对象扩展:
这样就可以实现对伴生对象扩展啦~~
三.接口:
接口定义的方法既可是抽象方法,也可是非抽象的方法。如果一个只读的属性没有定义getter方法,kotlin会自动为该属性添加abstract修饰符,如果一个读写属性没有定义getter和setter方法,kotlin会自动为该属性添加abstract修饰符。
Kotlin接口和java接口还有一个区别就是:Java接口中的所有成员都会自动使用public修饰符。如果要添加访问修饰符,则只能用public;如果不加修饰符,则系统默认是public。对于不需要被实现重写的成员呢,我们可以定义private或public修饰(在kotlin接口里,成员可以不必须是抽象的。方法可以定义实体)。默认的情况下是public修饰符代替。
因为本篇幅有点过长,还剩下枚举和类的委托没有介绍,打算在下一篇介绍。下篇近期会更新。如果感觉还不错的同学那么点波关注吧~~