一、抽象类和接口
对于抽象类和接口的话,和Java还是挺像的,虽然有一些区别,但是基本用法还是相同的。
在项目开发过程中,我们常会用到回调来获取数据,我们先创建一个接口类
interface OnResultListener{
fun success(value:Any);
fun fail(errorCode:Int,error:String);
}
很眼熟,对不对。首先是interface关键字,表示上述代码是一个接口类,success和fail分别是接口待实现的方法。我们创建一个NetOnResultListener,表示网络回调的类。
class NetOnResultListener:OnResultListener{
override fun success(value: Any) {
}
override fun fail(errorCode: Int, error: String) {
}
}
NetOnResultListener就是通过上面的方式继承了OnResultListener接口,并且通过关键字override实现了接口方法。其实接口也就这么多东西,下面我们说一下抽象类。项目中会经常用到控制器,所以我们先创建一个父类的控制器,如下:
abstract class BasePresenter {
abstract fun setOnResultListener(onResultListener: OnResultListener)
}
这个控制器中只有一个抽象方法,OnResultListener就是上面写的接口类,每个控制器都会实现setOnResultListener方法。那如何使用子类继承呢?如下代码
class AccountPresenter: BasePresenter() {
override fun setOnResultListener(onResultListener: OnResultListener) {
}
}
看到和接口不一样的地方了吗?是的,继承的话,类名后面有一个括号,而接口的话并没有。当然Kotlin也是遵循单继承多实现的原则,也就是只能继承一个父类,但是可以实现多个接口,和Java是一样的。
二、继承
关于继承我就不在用语言去过多的描述了,了解Java的话,都知道继承是怎么回事。其实关于继承,上面的代码有介绍,看下面的代码
abstract class Person(open val age:Int) {
abstract fun work();
}
class Doctor(age:Int):Person(age){
override val age:Int = 10
override fun work() {
println("doctor is working , his age is $age")
}
}
fun main(args: Array<String>) {
val doctor:Doctor = Doctor(26)
doctor.work()
}
Person类是一个父类,Doctor继承自Person。可以看到Person这个类有和方法前都有一个abstract关键字;如果想要覆写形参的话,参数名称前需要有一个open关键字,否则的话子类中的形参是不能被覆写的。如果父类是一个普通的类,不是抽象类怎么办呢?我们只需要将abstract关键字换成open即可,如下
open class Person(open val age:Int) {
open fun work(){
}
}
其实继承没有太多的东西可说的,主要就是上面这些。还有一些用法我们平时用的比较少,我就不在这里叙述了
三、类及其成员的可见性
Java中有四种修饰符:public protected default private,这四种修饰符大家应该很清楚它们的用法。
Kotlin中也有四种修饰符:public protected internal private
其中public protected private的用法和Java是相同的,但是有一点需要注意,Java中如果不写修饰符,默认为default修饰符,而Kotlin中默认的修饰符是public,这一点大家要记清楚哦
那internal这个修饰符怎么用的?如果写过Android项目的话,大家应该知道,除了app目录以外,也可能会写其他的library,尤其是用了ARouter框架的,分包是肯定的。而internal这个修饰表示的就是:在某个module中使用。
四、object
Kotlin中的object和Java中的Object可有不一样的含义。Kotlin中的object是一个关键字,用来创建类。如下:
object A{
fun a(){
println("a 方法")
}
}
fun main(args: Array<String>) {
A.a()
}
看到上面的代码估计有的小伙伴就蒙了。object是静态的意思吗?其实不是,object有如下几个特点:
(1)被object修饰的类只有一个实例对象,类似于Java中的单例
(2)不能够自定义构造方法。因为类似于Java中的单例,所以唯一的构造方法也被私有了,不能自定义操作。
(3)可以和类一样来实现接口和继承父类
知道上面的特点就能明白为什么可以像上面的代码那么写了吧
五、伴生对象与静态成员
到这里,很多小伙伴会问,Kotlin中没有静态方法和属性吗,其实是有的,只是和Java中的静态表现形式不太一样。看下面的代码:
fun main(args: Array<String>) {
StaticFun.staticFun1("字符串")
StaticFun.staticFun2(3.0)
println("i:${StaticFun.i}")
}
class StaticFun private constructor(){
companion object{
val i:Int = 50
fun staticFun1(str:String){
println("str:$str")
}
fun staticFun2(double:Double){
println("double:$double")
}
}
}
上述代码的StaticFun这个类中的staticFun1和staticFun2就是静态方法,i就是静态变量。看main方法中相应的使用和Java是一样的。我们看StaticFun这个类,首先constructor这个关键字前面有一个private,将构造方法变成了私有的;而静态方法staticFun1,staticFun2和静态变量i都写在了companion object这个代码块里。其实这个companin object产生的对象叫做伴生对象,和Java中的static用法差不多。如果在Java中引用Kotlin类的伴生对象和Kotlin这里的用法不太一样:
public class JavaMain {
public static void main(String[] args) {
StaticFun.Companion.staticFun1("字符串");
StaticFun.Companion.staticFun2(3.0);
System.out.println("i:"+StaticFun.Companion.getI());
}
}
你会发现,类和静态成员之间有一个Companion,虽然可以正常使用,但是感觉有些别扭,那怎么办,我们简单修改一下StaticFun中的代码:
class StaticFun private constructor(){
companion object{
@JvmField
public val i:Int = 50
@JvmStatic
fun staticFun1(str:String){
println("str:$str")
}
@JvmStatic
fun staticFun2(double:Double){
println("double:$double")
}
}
}
仔细观察,i变量上面有一个@JvmField的注解,而静态方法上面有@JvmStatic的注解。这样修改之后再Java中去掉Companion就可以使用了