六,类的进阶

2019-12-19  本文已影响0人  从心开始的我

1,类的构造器

当我们定义一个Person类时

class Person(var name:String,age:Int)

上面的定义相当于

class Person(var name: String, age: Int) {
    var age = 0
    init {
        this.age = age
        println(this.age)
    }
}
<1>init块

例如

class Person(var name: String, age: Int) {
    var age = 0
    init {
        this.age = age
        println(this.age)
    }
    var testa=3
    init {
        this.age=testa
        println(this.age)  
    }
}

他们会一起编译到函数的构造函数中一起执行

<2>类的属性必须初始化

Kotlin类的属性必须初始化,类的生命周期和属性的相同,这样使用者在调用类的属性时不会有负担

<3>类的继承

子类继承父类必须调用父类的构造方法

abstract class Anim

class Pig(var name: String):Anim(){
    //必须调用父类构造函数
}
<4>副构造器
abstract class Anim

class Pig(var name: String) : Anim() {
    //必须调用父类构造函数
    var age = 0;
    constructor(age: Int) : this("fff") {
        this.age = age
    }
}
不定义主构造器(不推荐)
abstract class Anim
class Dog : Anim{
    var name:String
    var age:Int
    constructor(name: String,age:Int):super(){
        this.name=name
        this.age= age
    }
}
一般情况下有这种情况的,比较推荐使用:主构造器+默认参数的形式
<5>工厂函数
fun main() {
    //Person的工厂方法
    Person("1111111")
    //String中系统的工厂方法
    String(charArrayOf('1'))
    //自定义String
    String(IntArray(2){ it })
}

val persons=HashMap<String,Person>()

fun Person(name: String):Person{
    //缓存person对象
    return persons[name]?:Person(name,25).also { persons[name] = it }
}

fun String(ints:IntArray):String{
    return ints.contentToString()
}

工厂函数和扩展方法:个人理解扩展方法的场景更大一些,而工厂函数的意义就是为了创建该类的对象,扩展方法可以返回该类对象也可以返回为Unit

2,类与成员的可见性

<1>模块的概念
<2> internal VS default
针对Kotlin中被internal修饰的类能被Java访问,可以通过一些特殊手段让其不能访问,例如下例:
internal class CoreApiKotlinA {
    @JvmName("%abcd")
    internal fun a(){
        println("Hello A")
    }
}
<3>构造器可见性
class Cat :Anim {
    var name: String
    var age: Int
    private constructor(name: String, age: Int):super() {
        this.name = name
        this.age = age
    }
}

可用于单例

<4>类的属性可见性
class Half (private var name: String,var age: Int){
    private var sex:Boolean=true
    init {
        println("$name---$age")
    }
}
//只能访问到age和sex
var half = Half("111", 2)
half.age=11

属性的getter和setter

- 属性的getter必须和属性的可见 一致

下例中 sex是public 所以不能把getter设置为private

class Half (private var name: String,var age: Int){
    var sex:Boolean=true
    private set
    //private get
}
- 属性的setter可见性不得大于属性的可见性
class Half(private var name: String, var age: Int) {
    private var firstName: String = ""
//    public set
}
<5>顶级声明的可见性(Kotlin独有)

3,类属性的延迟初始化

为什么要延迟初始化?

class Beer(var name: String,var age: Int){
   lateinit var firstName:String
}

常见的例子有:在Android中控件的初始化是在Activity的onCreate()方法中才能初始化,可以使用lateinit进行延迟初始化

lateinit的注意事项
lazy

4,代理 Delegate

接口代理

对象X 代替当前类A 实现接口B的方法

我 代替 你 处理了 它

属性代理

对象X代替属性a实现getter/setter方法

<1>接口代理

定义接口

interface InterFace {
    fun funA()
    fun funB()
    fun funC()
}

定义增强接口类

 class InterfaceWrapper(var inter:InterFace):InterFace{
     override fun funA() {
         inter.funA()
     }

     override fun funB() {
         inter.funB()
     }

     override fun funC() {
         println("---------------")
         inter.funC()
     }

代理形式

 class InterWrapper (private val inter: InterFace):InterFace by inter{
         override fun funC() {
             println("---------------")
         }
     }

接口 inter 代理 InterWrapper 实现 接口 InterFace 的方法

对于对象inter的唯一要求就是实现被代理的接口,因为如果inter没有实现接口方法的话,就不具有代理的能力

小案例 创建一个SupportArray

class SupperArray<E>(
    private val list: MutableList<E?> = ArrayList(),
    private val map: MutableMap<String, E> = HashMap()
) : MutableList<E?> by list, MutableMap<String, E> by map {
    override fun isEmpty(): Boolean {
        return list.isEmpty() && map.isEmpty()
    }

    override fun clear() {
        list.clear()
        map.clear()
    }

    override val size: Int
        get() = list.size + map.size

    override fun set(index: Int, element: E?): E? {
        if (list.size <= index) {
            repeat(index - list.size + 1) {
                list.add(null)
            }
        }
        return list.set(index, element)
    }

    override fun toString(): String {
        return "list:${list};map:${map}"
    }
}

SupportArray

使用SupperArray

fun main() {
    val supperArray = SupperArray<String>()
    supperArray.add("Hello")
    supperArray["Hello"] = "wwwwwwwwww"
    supperArray["Word"] = "fffffffffffff"
    supperArray[4] = "$$$$$$$$$"
    println(supperArray)
}

输出

list:[Hello, null, null, null, $$$$$$$$$];
map:{Word=fffffffffffff, Hello=wwwwwwwwww}
<2>属性代理
getter方法代理
class PersonNow(var sex: Boolean) {
    val firstName: String by lazy {
        if (sex)
            "nan"
        else
            "nv"
    }
}

源码分析

--lazy方法

public actual fun <T> lazy(initializer: () -> T): Lazy<T> = 
SynchronizedLazyImpl(initializer)

--Lazy.kt

@kotlin.internal.InlineOnly
public inline operator fun <T> Lazy<T>.getValue(thisRef: Any?, 
property: KProperty<*>): T = value

可以看到Lazy定义的方法 getValue

getValue(thisRef: Any?, property: KProperty<*>)

要想代理属性的getter需要重写该方法

setter方法代理

对于setter方法,其代理可分为两步:开始设置和设置之后

Delegates.observable代理设置之后
class Manger {
    var state:Int by Delegates.observable(0){
        property, oldValue, newValue ->
        println("$oldValue,$newValue")
    }
}
Delegates.vetoable代理设置之前
class Mantou {
    var ss: Int by Delegates.vetoable(0) { property, oldValue, newValue ->
        println("$oldValue,$newValue")
        newValue % 2 == 0
    }
}

调用

fun main() {
    val mantou = Mantou()
    mantou.ss = 1
    mantou.ss = 2
    mantou.ss = 3
    mantou.ss = 4
}

结果

0,1
0,2
2,3
2,4

Delegates.vetoable 给定初始化值后其结果返回值为Boolean,如果返回true表示允许本次对属性的设置,属性的值为newValue,返回false表示拦截此次设置值,属性的值仍然为oldValue

源码得知
Delegate.observable 返回 ReadWriteProperty

public interface ReadWriteProperty<in R, T> {
    public operator fun getValue(thisRef: R, property: KProperty<*>): T
    public operator fun setValue(thisRef: R, property: KProperty<*>, value: T)
}

对于var 来说需要实现下列方法,就可以代理属性

    public operator fun getValue(thisRef: R, property: KProperty<*>): T
    public operator fun setValue(thisRef: R, property: KProperty<*>, value: T)

对val来说 实现下方法即可代理属性

  public operator fun getValue(thisRef: R, property: KProperty<*>): T

具体例子

class Food {
    var x: Int by X()
}

class X {
    operator fun getValue(thisRef: Any?, property: KProperty<*>): Int {
        println("getValue")
        return 2
    }

    operator fun setValue(thisRef: Any?, property: KProperty<*>, i: Int) {
        println("setValue")
    }
}

fun main() {
    val food = Food()
    food.x=1;
    println(food.x)
}

运行结果

setValue
getValue
2

对food 的属性 x 调用其getter或者setter时会调用的属性代理

5,单例object

Kotlin中单例

public object Sigten {
    var x: Int = 0
    var y: String = ""
    fun mm() {}
}

Kotlin中访问

fun main() {
    Sigten.x = 3
    Sigten.y = "WWWWWWWW"
    println("${Sigten.x}||${Sigten.y}")
}

Java中访问

Sigten.INSTANCE.mm();
Sigten.INSTANCE.setX(1);
Sigten.INSTANCE.getX();

由于Kotlin是一门跨平台语言,不能因为Jvm上有静态变量就定义出相应的变量类型,这对于C语言和JavaScript行不通

@JvmStatic 模拟JVM平台的静态变量
public object Sigten {
    @JvmStatic var x: Int = 0
    @JvmStatic fun mm() {
    }
}

Java调用

Sigten.mm();
Sigten.setX(1);
Sigten.getX();
@JvmField 不让Kotlin属性生成getter和setter,直接访问属性
public object Sigten {
    @JvmStatic var x: Int = 0
    @JvmField var y:Int=0
    @JvmStatic fun mm() {
    }
}

Java调用

Sigten.mm();
Sigten.setX(1);
Sigten.getX();
Sigten.y=1;
伴生对象
class FoodX {
    companion object {
        @JvmStatic var x: Int = 0
        @JvmStatic fun mm() {
        }
    }
}

想当于Java中

public class FoodX {
    public static int x = 0;
    public static void mm(){}
}

6,内部类

class Outer{
    //内部类
    inner class Inner(){}
    //静态内部类
    class StaticInner(){}
}

使用

fun main() {
    //创建非静态内部类
    var inner = Outer().Inner()
    //创建静态内部类
    var staticInner = Outer.StaticInner()
}
<1>内部object
object OuterObject {
    object InnerStaticObject {
        var x: Int = 0
    }
}

使用

OuterObject.InnerStaticObject.x=3
<2>匿名内部类
object :Runnable{
        override fun run() {
        }
    }
和Java中不同的是,Kotlin中匿名内部类支持多实现或继承父类

例如

object :Runnable,Cloneable{
        override fun run() {
        }
    }
Java中也可以实现方法中多继承 使用LocalClass
public class TestObjeck {
    public static void main(String[] args) {
        class LocalClass implements Cloneable,Runnable{
            @Override
            public void run() {
            }
        }
    }
}
Kotlin中不仅支持LocalClass 还支持localMethod(方法中定义方法)
  object :Runnable,Cloneable{
        override fun run() {
        }
    }

    class LocalClass:Runnable,Cloneable{
        override fun run() {
        }
    }

    fun localMethod(){}

7,数据类 data class

component: 定义在主构造器中的属性又被称为component

data class TT(val name: String, val age: Int) {}
fun main() {
    var tt = TT("", 4)
}
如何合理的使用data class

为了实现一些特殊的需求,需要data class 有无参构造和可以被继承,官方提供了 noArg 和allOpen 插件进行支持

id 'org.jetbrains.kotlin.plugin.noarg' version '1.3.60'
id 'org.jetbrains.kotlin.plugin.allopen' version '1.3.60'

8, 枚举类 enum class

无参构造枚举

enum class CC {
    Idle, Busy
}

有参构造枚举

enum class DD(arg: Int) {
    Idle(0), Busy(1)
}

枚举实现接口--统一实现方式

enum class FF : Runnable {
    Idle, Busy;

    override fun run() {
    }
}

枚举实现接口--单独实现方式

enum class EE : Runnable {
    Idle {
        override fun run() {
        }
    },
    Busy {
        override fun run() {
        }
    };
}
<1>因为enum 枚举本身是个类 所以可以为其定义扩展方法
<2>枚举本事是可数的所在when 条件语句中可以不加else
<3> 因为枚举本身有顺序的概念所以可以对其进行比较大小
<4> 枚举有顺序所以也有区间概念
enum class Color {
    White, Red, Black, Pink, Ori, Yellow
}
fun main() {
    val ran = Color.Black..Color.Yellow
    val color = Color.White
    color in ran
}

9,密封类 sealed class

例子

sealed class PlayState

object Idle : PlayState()

object Playing : PlayState() {
    fun start() {}
    fun stop() {}
}

object Error : PlayState() {
    fun recover() {}
}

使用上面定义的几个类型做个小案例

class Player {
    var state: PlayState = Idle

    fun play() {
        this.state = when (val state = this.state) {
           is Idle -> {
                Playing.also {
                    it.start()
                }
            }
            is Playing -> {
                state.stop()
                state.also {
                    it.start()
                }
            }
            is Error -> {
                state.recover()
                Playing.also {
                    it.start()
                }
            }
        }
    }
}
如何区分使用密封类还是枚举呢
  1. 如果需要进行类型区分则是使用密封类,如果要进行值的区分 则用枚举
  2. 枚举类不能创建对象,而密封类可以创建对象

枚举不能创建对象这点个人理解起来比较难一点,为什么不能枚举就不能创建对象呢?
因为枚举就是实例对象的存在<O(∩_∩)O哈哈~>
而密封类则不同,他的分支是类型,只要是子类的对象就行,因此可以创建多个对象

10,内联类 inline class

使用注意
  1. 内联类不能有backingfiled 只能有方法
  2. 内联类可以实现接口,但不能继承父类也不能被继承

例子

inline class BoxInt(private val value: Int) {
    operator fun inc(): BoxInt {
        return BoxInt(value + 1)
    }
}
内联类的使用场景
上一篇 下一篇

猜你喜欢

热点阅读