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基础语法总结