Kotiln基础语法总结

Posted narkang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kotiln基础语法总结相关的知识,希望对你有一定的参考价值。

一、主构造函数


我们在Player类的定义头中定义一个主构造函数,使用临时变量为Player的各个属性提供初始值,在Kotlin中,为便于识别,临时变量(包括仅引用一次的参数),通常都会以下划线开头的名字命名

class Player(
        _name: String,
        _age: Int,
        _isNormal: Boolean
)

    var name = _name
        get() = field.capitalize()
        private set(value) 
            field = value.trim()
        

    var age = _age
    var isNumber = _isNormal



1.1、主构造函数里定义属性

Kotlin允许你不使用临时变量赋值,而是直接用一个定义同时指定参数和类属性,通常,我们更喜欢用这种方式定义类属性,因为他会减少重复代码。

class Player(
        _name: String,
        var age: Int,
        val isNormal: Boolean
)
    //属性不需要赋值
    var name = _name
        get() = field.capitalize()
        private set(value) 
            field = value.trim()
        

    //次构造函数
    constructor(name: String): this(name, 100, false)
        this.name = name.toUpperCase()
    
    
	

1.2、默认参数

定义构造函数时,可以给构造函数参数指定默认值,如果用户调用时不提供值参,就使用这个默认值。

1.3、初始化块

初始化快可以设置变量或值,以及执行有效性检查,如检查传给某构造函数的值是否有效,初始化代码会在构造类实例时执行。

init


1.4、初始化顺序

1.主构造函数里声明的属性
2.类级别的属性赋值
3.init初始化快里的属性赋值和函数调用
4.次构造函数里的属性赋值和函数调用

1.5、延迟初始化

class Player

    lateinit var equipment: String

    fun ready()
        equipment = "sharp knife"
    

    fun battle()
        //检查是否初始化了
        if(::equipment.isInitialized) println(equipment)
    


1.6、惰性初始化

延迟初始化并不是推后初始化的唯一方式,你也可以暂时不初始化某个变量,直到首次使用它,这叫做惰性初始化。

class Player(_name: String)

    var name = _name

    val config by lazy  loadConfig() 

    private fun loadConfig(): String
        println("loading")
        return "xxx"
    

二、初始化陷阱


2.1、陷阱一

在使用初始化块时,顺序非常重要,你必须保证块中的所有属性已完成初始化

class Player()
    //这个必须放在初始化代码之前
    val blood = 100
    init 
        val bloodBonus = blood.times(4)
    

2.2、陷阱二

这段代码编译没有问题,因为编译器看到name已经在init块里初始化了,但代码一运行,就会抛出空指针异常,因为name属性还没赋值,firstLetter函数就应用它了。

class Player()
    var name: String
    private fun firstLetter() = name[0]

    init 
        println(firstLetter())
        name = "jack"  //jack放在这里报空指针异常
    

2.3、陷阱三

在用initPlayerName函数初始化时,name属性还未完成初始化

class Player(_name: String)

	//这里赋值的时候,name还未初始化
    var playerName: String = initPlayerName()

    val name: String = _name
    private fun initPlayerName() = name


三、继承


类默认都是封闭的,要让某个类开放继承,必须使用open关键字修饰它

open class Product(val name: String)
    fun description() = "Product:$name"

    open fun load() = "Nothing..."



class LuxuryProduct: Product("Luxury")

    override fun load() = "LuxuryProduct loading"



fun main()
    val p = LuxuryProduct()
    println(p is Product) //true
    println(p is LuxuryProduct) //true
    //无须在代码里显示指定,每一个类都会继承一个共同的叫作Any的超类
    println(p is Any) //true

3.1、类型转换

编译器允许不经类型转换直接使用

fun sale(p: Product)
    println(p.load())


fun main()
    val p = LuxuryProduct()
    sale(p as Product)

四、object


使用object关键字,你可以定义一个只能产生一个实例的类-单例
使用object关键字有三种方式
1.对象声明
2.对象表达式
3.伴生对象

4.1、单例

object ApplicationConfig

    init
        println("loadingConfig..")
    

    fun setSomething()
        println("set something")
    


4.2、对象表达式(匿名内部类)

open class Player
    open fun load() = "Nothing..."


fun main()
    //匿名内部类
    val p = object : Player()
        override fun load() = "anoymous class load..."
    

    println(p.load())

4.3、伴生对象

如果你想将某个对象的初始化和一个类实例捆绑在一起,可以考虑使用伴生对象,使用companion修饰符,你可以在一个类定义里声明一个伴生对象,一个类里只能有一个伴生对象。

open class ConfigMap
	//只有初始化ConfigMap类或调用load函数时,伴生对象的内容才会载入。
	//而且无论实例化ConfigMap类多少次,这个伴生对象始终只有一个实例存在
    companion object
        private const val PATH = "xxx"
		
        fun load() = File(PATH).readBytes()

    


五、嵌套类


如果一个类只对另一个类有用,那么将其嵌入到该类中并使这两个类保持在一起是合乎逻辑的,可以使用嵌套类

class Player
    private class Equipment(var name: String)
        fun show() = println("Equipment:$name")
    

    fun battle()
        Equipment("sharp knife").show()
    

六、数据类


数据类,是专门设计用来存储数据的类
数据类提供了toString的个性化实现
==符号默认情况下,比较对象就是比较它们的引用值,数据类提供了equals和hashCode的个性化实现

//会自动重写toString方法
data class Coordinate(var x:Int, var y:Int)
    var isInBounds = x > 0 && y > 0

6.1、copy

除了重写Any类的部分函数,提供更好用的默认实现外,数据类还提供了一个函数,它可以用来方便地复制一个对象。

使用数据类的条件:
正是因为上述这些特性,你才倾向于用数据类来表示存储数据的简单对象,对于那些经常需要比较、复制或打印自身内容的类,数据类尤其适合它们,然而,一个类要成为数据类,也要符合一定条件。总结下来,主要有三个方面:
1.数据类必须有至少带一个参数的主构造函数
2.数据类主构造函数的参数必须是val或var
3.数据类不能使用abstract、open、sealed和inner修饰符

七、解构声明


解构声明的后台实现就是component1、compent2等若干个组件函数,让每个函数负责管理你想返回的一个属性数据,如果你定义一个数据类,它会自动为所有定义在主构造函数的属性添加对应的组件函数。

class PlayerScore(val experience: String, val level: Int)
    operator fun component1() = experience
    operator fun component2() = level


fun main() 
    val (x, y) = PlayerScore("e", 10)

八、枚举


枚举类,用来定义常量集合的一种特殊类,也可以定义函数

data class Coordinate(var x:Int, var y:Int)
enum class Direction(val coordinate: Coordinate)
    EAST(Coordinate(10, 10)),
    WEST(Coordinate(20, 20)),
    SOUTH(Coordinate(30, 30)),
    NORTH(Coordinate(40, 40));

    fun updateCoordinate(playerCoordinate: Coordinate)
        = Coordinate(coordinate.x + playerCoordinate.x, coordinate.y + playerCoordinate.y)

	//当两个对象equals为true时,hashCode值也应该相等

九、运算符重载


如果将内置运算符应用在自定义类身上,你必须重写运算符函数,告诉编译器该如何操作自定义类。

class Coordinate(var x:Int, var y:Int)
    var isInBounds = x > 0 && y > 0;

    operator fun plus(other: Coordinate)
        = Coordinate(x + other.x, y + other.y)


常见操作符

十、代数数据类型


可以用来表示一组子类型的闭集,枚举类就是一种简单的ADT

enum class LincenseStatus
    UNQUALIFIED,
    LEARNING,
    QUALIFIED


class Diriver(var status: LincenseStatus)
    fun checkLicense(): String
        return when(status)
            LincenseStatus.UNQUALIFIED -> "没资格"
            LincenseStatus.LEARNING -> "在学"
            LincenseStatus.QUALIFIED -> "有资格"
        
    

十一、密封类

对于更复杂的ADT,你可以使用Kotlin的密封类(sealed class)来实现更复杂的定义,密封类可以用来定义一个类似枚举类的ADT,但你可以更灵活的控制某个子类型。
密封类可以用若干子类,要继承密封类,这些子类必须和它定义在同一个文件里

sealed class LicenseStatus
    object Unqualified: LicenseStatus()
    object Learning: LicenseStatus()
    class Qualified(val licenseId: String): LicenseStatus()


class Diriver(var status: LicenseStatus)
    fun checkLicense(): String
        return when(status)
            is LicenseStatus.Unqualified -> "没资格"
            is LicenseStatus.Learning -> "在学"
            is LicenseStatus.Qualified -> "有资格,驾驶证编号:$(this.status as LicenseStatus.Qualified).licenseId"
        
    

十二、接口


Kotlin规定所有的接口属性和函数实现都要使用override关键字,接口中定义的函数并不需要open关键字修饰,他们默认就是open的。

interface Movable

    //只要你愿意,你可以在接口里提供默认属性的getter方法和函数实现
    val maxSpeed: Int
        get() = (1..400).shuffled().last()
    var wheels: Int

    fun move(movable: Movable): String



class Car(_name: String,
          override var wheels: Int = 4
          ): Movable

    override var maxSpeed: Int
        get() = super.maxSpeed
        set(value) 
        

    override fun move(movable: Movable): String 
        return ""
    


十三、抽象类


要定义一个抽象类,你需要在定义之前加上abstract关键字,除了具体的函数实现,抽象类也可以包含抽象函数–只有定义,没有函数实现。

abstract class Gun(val range: Int)

    abstract fun pullTrigger(): String



class AK47(val price: Int): Gun(500)

    override fun pullTrigger(): String 
        TODO("Not yet implemented")
    


十四、泛型


class MagicBox<T>(item: T)
    private var subject: T = item


class Boy(val name: String, val age: Int)

class Dog(val weight: Int)

fun main()
    MagicBox<Boy>(Boy("Jack", 20))
    MagicBox<Dog>(Dog(20))

注意:
泛型参数通常用字母T(代表英文type)表示,当然,想用其他字母,甚至是英文单词都是可以的。不过,其他支持泛型的语言都是用这个约定俗成的T,所以建议你继续用它,这样写出的代码别人更容易理解。

14.1、泛型函数

泛型参数也可以用于函数
定义一个函数用于获取元素,当且仅当MagicBox可用时,才能获取元素。

class MagicBox<T>(item: T)
    var available = false
    private var subject: T = item

    fun fetch(): T?
        return subject.takeIf  available 
    


class Boy(val name: String, val age: Int)

fun main()
    val magicBox = MagicBox<Boy>(Boy("Jack", 20))
	magicBox.available = true
    magicBox.fetch()?.run 
        println("you find $name")
    

14.2、多泛型参数

泛型函数或泛型类也可以有多个泛型参数。

class MagicBox<T>(item: T)
    var available = false
    private var subject: T = item

    fun fetch(): T?
        return subject.takeIf  available 
    

    //业务,元素进行修改
    fun <R> fetch(subjectModFunction: (T) -> R): R?
        return subjectModFunction(subject).takeIf  available 
    


class Boy(val name: String, val age: Int)

class Man(val name: String, val age: Int)


fun main()
    val magicBox = MagicBox<Boy>(Boy("Jack", 20))

    magicBox.fetch()?.run 
        println("you find $name")
    

    magicBox.fetch
        Man(it.name, it.age.plus(15))
    

14.3、泛型类型约束

如果要确保MagicBox里面只能装指定类型的物品,如Human类型,怎么办?

class MagicBox<T: Human>(item: T)
    var available = false
    private var subject: T = item

    fun fetch(): T?
        return subject.takeIf  available 
    

    //业务,元素进行修改
    fun <R> fetch(subjectModFunction: (T) -> R): R?
        return subjectModFunction(subject).takeIf  available 
    


open class Human(val age: Int)

class Boy(val name: String, age: Int): Human(age)

class Man(val name: String, age: Int): Human(age)

14.4、vararg关键字与get函数

MagicBox能存放任何类型的Human实例,但一次只能放一个,如果需要放多个实例了?

class MagicBox<T: Human>(vararg item: T)
    var available = false
    private var subject: Array<out T> = item

    fun fetch(index: Int): T?
        return subject[index].takeIf  available 
    

    //业务,元素进行修改
    fun <R> fetch(index: Int, subjectModFunction: (T) -> R): R?
        return subjectModFunction(subject[index]).takeIf  available 
    


open class Human(val age: Int)

Kotiln基础语法总结

Kotiln基础语法总结

Kotiln基础语法总结

Kotiln基础语法总结

Kotiln基础语法总结

Kotiln基础语法总结