Kotlin学习笔记(三)—面向对象(2)

传送门🚪:
Kotlin学习笔记(三)—面向对象(1)Kotlin学习笔记(三)—面向对象(2)Kotlin学习笔记(三)—面向对象(3)

7.类及其成员的可见性

我们直接用一张表格和Java语言类比:

Java

Kotlin

private

private

protected

protected

-

default(包内可见)

internal(模块内可见)

-

public

public

8.object

在Kotlin中,object代表只有一个实例的类,即通常所说的单例。用object关键字定义的类可以继承父类,也可以实现接口,但不能自定义构造函数,通过Kotlin字节码反编译其本质上就是单例模式(饿汉式)
kotlin代码如下:

object home{
        
    }

通过反编译后的.java文件如下:

public static final class home {
      @NotNull
      public static final object.home INSTANCE;

      private home() {
      }

      static {
         object.home var0 = new object.home();
         INSTANCE = var0;
      }
   }

9.伴生对象和静态方法

在java中,有一些工具类是不需要属性去支撑的,所以它们里面的方法大多数都以静态方法的形式存在。
在Koltin中,无法直接声明静态方法,只能委托伴生对象去实现静态方法。

class Method private constructor(val params:Int){
    
    //伴生对象
        companion object{
        //静态方法
            fun build( params: Int):Method{
                return Method(params)
            }
    }
}

fun main() {
    val method = Method.build(1)
}

如果你需要在java代码中调用这个方法,

public class test {

    public static void main(String[] args) {
        Method build = Method.Companion.build(1);
    }
}

写起来比较复杂,如果需要频繁调用的话,可以在对应的方法上加注解 @JvmStatic,这样就可以直接使用类名.方法名的方式去访问这个方法。
同样,静态变量也是类似的

class Method private constructor(val params: Int) {

    //伴生对象
    companion object {
        @JvmStatic       //静态方法
        fun build(params: Int): Method {
            return Method(params)
        }

        @JvmField //静态变量
        val TAG = "Method"
    }

}

fun main() {
    val method = Method.build(1)
    println(Method.TAG)
}

在加了 @JvmField 注解后,在java代码中就可以直接使用了。

public class test {

    public static void main(String[] args) {
        Method build = Method.build(1);
        System.out.println(Method.TAG);
    }
}

在kotlin中,更推荐使用包级变量或者包级方法。

10.方法重载与默认参数

10.1 方法重载

方法重载的特征总结起来只有一句话:同名不同参数
也就是说

fun test(int:Int){
        
    }
    fun test(str:String){

    }

这样的方法就叫做方法重载。方法能够重载的根本原因是他们的方法签名不同,方法签名主要由方法名和参数列表组成。
要注意,如果两个方法的方法名和参数列表相同,但是返回值的类型不相同,那么这两个方法不能重载。

###10.2默认参数
在定义方法时,可以给形参列表赋值,那么在调用方法时,就可以不传这个值,编译器会自动将默认值当做实参。例如:

class Method{
    fun test(int: Int = 1) {
        println("int: $int")
    }

    fun test(str0: String, str: String = "qwerdf") {
        println("str0: $str0  str:  $str")
    }

}

fun main() {
    val method = Method(1)
    method.test(1)
    method.test("123")
}

运行结果:

android kotlin 对象继承 kotlin object class_android kotlin 对象继承


方法重载一般会搭配默认参数进行实现,这一点在我们重载一些构造函数的时候非常常见。

在Java代码中,如果调用Kotlin类中有默认参数的方法,需要使用 @JvmOverloads 注解去注解有默认参数的方法,否则就必须传入参数。

11.扩展方法与扩展属性

11.1 扩展方法

在java代码中,我们经常需要写一些工具类去完成我们较为常用的功能,例如多次打印一个字符串,在java中是这样的:

public static void printTimes(String arg, int times) {
        for (int i = 0; i < times; i++) {
            System.out.println(arg);
        }
    }

而在Koltin中,我们可以直接定义一个扩展方法:

fun main() {
    val name = "panghu"
    name.printTimes(10)

}

fun String.printTimes(times: Int) {
    for (i in 0 until times) {
        println(this)
    }
}

我们甚至可以使用之前运算符重载的方法

fun main() {
    val name = "panghu"
    name *10
}

operator fun String.times(times: Int) {
    for (i in 0 until times) {
        println(this)
    }
}

代码一下子变得清爽起来了。然后在java中,如果需要使用这个扩展方法,则需要

public static void main(String[] args) {
      ExtendKt.times("panghu",10);
    }
}

通过类名去调用。在kotlin反编译的java文件中,其实也是这样去实现的。

11.2扩展属性

扩展属性的定义与扩展方法很像,但有一点需要注意的是扩展属性没有backingfiled,不能被初始化。这点和我们在接口中定义属性是一样的。我们看个例子:

fun main() {
    println("panghu".no)
    println("panghu".zero)

}


val String.no: String
    get() = "NO"

var String.zero: Int
    set(value) {}
    get() = 0

12.属性代理

我们在上边提到过接口代理 使用了by关键字。属性代理也是使用by关键字。
在kotlin中,我们见过:

val name by lazy { 
        "hello"
    }

这里的lazy就是属性代理。而想要实现属性代理,就必须实现getValue方法。也就是说如果通过属性代理的方式声明属性,那个这个属性的get方法就会调用代理的getValue方法
自定义的一个String类型的代理:

class P{
    private var value:String?=null
    operator fun getValue(thisRef:Any?, property:KProperty<*>):String{
        println("getValue $thisRef --->${property.name}")
        return value?:""
    }
    operator fun setValue(thisRef:Any?,property:KProperty<*>, value:String){
        println("setValue $thisRef --->${property.name} === $value")
        this.value = value
    }
}

当我们使用代理属性初始化后,在进行读取或者赋值的时候回自动 调用getValue和setValue方法。
完整的案例:

class Extend {
    val name by lazy {
        "helloword"
    }
    val name2 by P()
    var name3 by P()
}
class P{
    private var value:String?=null
    operator fun getValue(thisRef:Any?, property:KProperty<*>):String{
        println("getValue $thisRef --->${property.name}")
        return value?:""
    }
    operator fun setValue(thisRef:Any?,property:KProperty<*>, value:String){
        println("setValue $thisRef --->${property.name} === $value")
        this.value = value
    }
}
fun main() {
    var extend = Extend()
    println(extend.name)
    println(extend.name2)
    println(extend.name3)
    extend.name3 = "23456"
    println(extend.name3)

}

运行结果如下:

android kotlin 对象继承 kotlin object class_伴生对象_02


其中 Extend@5a10411是我们创建的那个一个对象