终于到了Kotlin的核心篇章了,Kotlin的定义类,以及对象 接口 抽象类等等,这些知识点在Java中也是至关重要的
【定义类】
field
针对你定义的每一个属性,Kotlin都会产生一个field、一个getter、以及一个setter,field用来存储属性数据,你不能直接定义field, Kotin会封装field,保护它里面的数据,只暴露给getter和setter 使用。属性的getter方法决定你如何读取局性值,每个属性都有getter方法,setter方法决定你如何给属性赋值,所以只有可变属性才会有setter方法,尽管Kotin会自动提供默认的gete和seter方法,但在需要控制如何读写属性数据时,你也可以自定义他们。
class Player {
var name = "abc"
get() = field.capitalize()
set(value) {
field = value.trim()
}
var age = 10
get() = field.absoluteValue
private set(value) {
field = value.absoluteValue
}
}
初始化
在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 isNormal = _isNormal
}
主构造函数里定义属性
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, age = 20, isNormal = false)
默认参数
定义构造函数时,可以给构造函数参数指定默认值,如果用户调用时不提供值参,就使用这个默认值。
class PlayerOne(_name: String, var age: Int= 10, val isNormal:Boolean ) {
var name = _name
get() = field.capitalize()
private set(value) {
field = value.trim()
}
}
初始化块
初始化块可以设置变量或者值,以及执行有效性检查,如检查给某个构造函数的值是否有效,初始化块代码会在构造类实例时执行。
init {
require(age > 10) { "age must be positive" }
require(name.isNotBlank()) { "必须有个名字" }
}
初始化顺序
主构造函数里声明的属性 类级别的属性赋值 init初始化块里的属性赋值和函数调用 次构造函数里的属性赋值和函数调用 延迟初始化
使用lateinit关键字相当于做了一个约定:在用它之前负责初始化 只要无法确认lateinit变量是否完成初始化,可以执行isInitalized检测
class Player2 {
lateinit var equipment:String
fun ready(){
equipment = "sharp knife"
}
fun battle(){
if (::equipment.isInitialized) println(equipment)
}
}
惰性初始化
延迟初始化并不是推后初始化的唯一方式,你可以暂时不出初始化某个变量,直到首次使用它,这个叫做惰性初始化。
class Player3(_name: String) {
var name = _name
val config by lazy { loadConfig() }
private fun loadConfig():String{
return "loading"
}
}
【继承】
Kotlin类默认都是封闭的,要让某个类开放继承,必须使用open关键字修饰它。
open class Animal(val name: String){
fun MyName() = "名字是: $name"
fun Eat() = "eat...."
}
class People :Animal("大卫")
函数重载
父类的函数也要以open关键字修饰,子类才能覆盖它。
类型转换
as操作符声明,这是一个类型转换
fun main() {
val people = People()
sale((people as Animal))
}
fun sale(p:Animal){
p.Eat()
}
【对象】
objec 关键字
使用object关键字,你可以定义一个只能产生一个实例的类----单例 使用object关键字有三种方式 (1) 对象声明 (2) 对象表达式 (3) 伴生对象
伴生对象
如果你想将某个对象的初始化和实体类捆绑在一起,可以考虑伴生对象,使用companion修饰符,你可以在一个类定义里声明一个伴生对象,一个类只能有一个伴生对象。
open class ConfigMap{
companion object{
private const val Path = "sss"
fun load()= File(Path).readBytes()
}
}
嵌套类
如果一个类对另一个类有用,那么将其嵌入到该类中并使这两个类保持在一起合乎逻辑,可以使用嵌套类。
class Player2 {
class Equipment(var name: String){
fun show() =println("equipment $name")
}
fun battle(){
Equipment("knife").show()
}
}
数据类
数据类,是专门用来存储数据的类 数据类提供了toString的个性化实现 == 符号默认情况下,比较对象就是比较他们的引用值,数据提供了equals 和hasCode的个性化实现
fun main() {
println(Coor(1,5))
}
data class Coor(var x: Int, var y: Int) {
var isInBounds = x >= 0 && y >= 0
}
使用数据类的条件
数据类必须有至少带一个参数的主构造函数 数据类主构造函数的参数必须是val 或者var 数据类不能使用abstract open sealed 和 inner修饰符
运算符重载
操作符 | 函数名 | 作用 |
---|
+ | plus | 把一个对象添加到另一个对象里 | += | plusAssign | 把一个对象添加到另一个对象里,然后将结果赋值给第一个对象 | > | compareTo | 如果左面对象大于右面对象,返回true 否则返回false | [ ] | get | 返回集合中指定的元素位置 | … | rangeTo | 创建一个range对象 | in | contains | 如果对象在集合里返回true |
【接口】
接口定义 Kotlin规定所有的接口属性和函数实现都要使用override关键字,接口定义的函数并不需要open关键字修饰,他们默认就是open.
interface Movable{
var wheels:Int
fun move(movable:Movable):String
}
class Car(_name: String,override var wheels:Int = 4):Movable{
override fun move(movable: Movable): String {
TODO("Not yet implemented")
}
}
抽象类 要定义一个抽象类,需要在定义之前加上abstract关键字,除了具体的函数实现,抽象类也可以包含抽象函数一直有定义,没有函数实现。
abstract class Gun(val range: Int) {
abstract fun trigger(): String
}
class AK47(_price:Int):Gun(range = 100){
override fun trigger(): String = "AK47 shooting"
}
核心点:定义类 接口 抽象类
|