一、类的定义
class Test {
}
二、构造函数
主构造函数:
class Test(param1 : String, param2 : Int) {
init {
println("param1:$param1")
println("param2:$param2")
}
}
还可以添加constructor关键字
class Test constructor(param1 : String, param2 : Int) {
init {
println("param1:$param1")
println("param2:$param2")
}
}
次构造函数:
class Test {
constructor() { }
constructor(param1 : String, param2 : Int) { }
}
如果存在主构造函数,必须继承主构造函数:
class Test (param1 : String, param2 : Int) {
constructor(param1 : String, param2 : Int, param3 : Int) :this(param1, param2) {
}
}
初始化块(init)和次构造函数的执行顺序:
我们需要知道,init代码块是主构造函数初始化的地方,当主构造函数被实例化的时候,init代码块必然执行。
当次构造函数被实例化的时候,init代码块首先被执行,然后才会执行次构造函数的实现。
私有构造函数:
class Test private constructor(param1 : String, param2 : Int) {
private constructor(param1 : String, param2 : Int, param3 : Int) :this(param1, param2) {
}
}
构造函数时可以被私有的,如果被私有,外部将不能访问,也就是说,在外部,不能对该构造方法进行实例化。
private 关键字放在 constructor 前面,默认情况下是 public,public情况下,主构造函数的 constructor 关键字可以被省略。
三、继承
Kotlin和Java不同,Kotlin任何一个非抽象类默认不可以继承,添加 open 关键字可以让非抽象类继承。
open class Person {
...
}
例如:
class Student(param1: String, param2: Int) : Person(param1, param2) {
}
open class Person constructor(param1: String, param2: Int){
}
kotlin中函数的重载(覆盖):
open class Person constructor(param1: String, param2: Int){
fun doSomething(){
println("Person doSomething")
}
}
class Student(param1: String, param2: Int) : Person(param1, param2) {
}
val person: Person = Student("zhangsan", 13)
person.doSomething()
打印结果是:
Person doSomething
在子类Student中可以重载父类的方法:
class Student(param1: String, param2: Int) : Person(param1, param2) {
override fun doSomething(){
println("Student doSomething")
}
}
在kotlin中,子类默认是不允许重载父类的方法,如果想要重载,父类的方法必须使用open关键字修饰:
open class Person constructor(param1: String, param2: Int){
open fun doSomething(){
println("Person doSomething")
}
}
如此,打印结果是:
Student doSomething
变量的覆盖:
在父类中,存在一个成员变量 name
open class Person constructor(param1: String, param2: Int){
val name: String = "zhangsan"
}
在子类中可以访问父类的成员变量:
class Student(param1: String, param2: Int) : Person(param1, param2) {
fun doSomething(){
println("my name is $name")
}
}
如果子类中定义一个和父类成员变量一模一样的成员变量,那么父类的成员变量就被子类的成员变量覆盖了:
open class Person constructor(param1: String, param2: Int){
open val name: String = "zhangsan"
}
class Student(param1: String, param2: Int) : Person(param1, param2) {
override val name: String = "lisi"
fun doSomething(){
println("my name is $name")
}
}
父类的成员变量使用 open 修饰, 子类的成员变量使用 override 修饰。
派生类初始化顺序:总是首先初始化基类,然后才会初始化派生类
抽象类:
abstract class Person {
}
使用 abstract 修饰表示一个抽象类,抽象类不能被实例,只能被继承,而 open 关键字修饰的类既可以继承,也可以被实例化。
四、get 和 set 函数
在Java中,定义一个bean是常见的,在bean里面存在 set 和 get 函数,
kotlin中 set 和 get 的语法和ava存在很大的区别:
class Student {
var name: String
get() = name
set(value) {
this.name = value
}
}
在 kotlin 中, set 和 get 函数可以写在成员变量后面。
如上代码,set 和 get 没有其它特别处理,可以将 set 和 get 省略。
五、延迟初始化
private lateinit var textView: TextView
当你对一个全局变量使用了lateinit关键字时,请一定要确保它在被任何地方调用之前已经完成了初始化工作,
否则Kotlin 将无法保证程序的安全性。
判断是否已经初始化
if (::textView.isInitialized)
六、接口
接口的定义和声明:
interface MyInterface {
fun doSomething()
}
接口的实现:
class Student : MyInterface{
override fun doSomething() {
TODO("Not yet implemented")
}
}
kotlin 和 java一样,都支持多个接口实现。
六、可见性修饰符
public、private、protected和internal
默认修饰符是 public
internal:只对同一模块下可见
七、数据类
使用 data 关键字,声明该类为数据类
data class Cellphone(val brand: String, val price: Double)
Kotlin会根据主构造函数中的参数帮你将equals()、hashCode()、toString()等
且无实际逻辑意义的方法自动生成,从而大大减少了开发的工作量。
八、密封类
// sealed:密封类,同时具有枚举和普通类的特性
// 密封类及其子类必须声明在同一个 kotlin 文件中
sealed class Result // 反编译得到抽象类
class Success(val code: Int) : Result()
class Exception(val code: Int, val message: String) : Result()
// 在密封类Result中,只定义了两个子类,所以在使用when语句时,不需要else
fun handleResult(result: Result): String{
return when(result) {
is Success -> {
"success"
}
is Exception -> {
"exception"
}
}
}
九、内部类和嵌套类
Kotlin 内部类与嵌套类的区别是:
1、内部类会带有一个外部类的对象的引用,嵌套类则没有
2、内部类需要使用 inner class 定义,而嵌套类则使用 class 定义
Kotlin 内部类会带有一个对外部类的对象的引用,所以内部类可以访问外部类成员属性和成员函数。
Kotlin 内部类使用 this@[外部类名] 持有外部类对象的引用。
十、委托类
类委托:于将一个类的具体实现委托给另一个类去完成
interface Base {
fun fixbug()
}
class ZhangsanDo : Base {
override fun fixbug() {
println("zhangsan fixed a bug")
}
}
class Lisi : Base {
override fun fixbug() {
println("lisi fixed a bug")
}
}
假设有一个bug, 张三可以解决bug,李四也可以解决bug,如果领导将bug交给张三解决:
val base1 : Base = ZhangsanDo()
base1.fixbug()
如果领导将bug交给李四给解决:
val base2 : Base = Lisi()
base2.fixbug()
但是,存在一种情况,就是李四无法解决这个bug,所以他想委托为张三去解决:
interface Base {
fun fixbug()
}
class ZhangsanDo : Base {
override fun fixbug() {
println("zhangsan fixed a bug")
}
}
class Lisi : Base by ZhangsanDo() // 委托给张三
如果领导将bug交给张三,张三直接解决:
val base1 : Base = ZhangsanDo()
base1.fixbug()
如果领导将bug交给李四,李四将bug委托给张三,本质上,bug是由张三解决的:
val base2 : Base = Lisi()
base2.fixbug()
十一、委托属性
class Example {
var p: String by Delegate() // 将p属性的具体实现委托给了Delegate类去完成
}
class Delegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return "$thisRef, thank you for delegating '${property.name}' to me!"
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
println("$value has been assigned to '${property.name}' in $thisRef.")
}
}
当 p 使用时就会自动执行 Delegate 的 getValue 方法,当 p 被赋值时,就会自动执行 Delegate 的 setValue方法。
val e = Example()
println(e.p)
打印结果是:
Example@234bef66, thank you for delegating 'p' to me!
val e = Example()
e.p = "NEW"
打印结果是:
NEW has been assigned to 'p' in Example@234bef66.
我们可以发现,当对象第一次被调用时:执行getValue函数,当对象第一次赋值时执行setValue方法。
下面使用委托属性,实现懒加载:
class Later<T>(val block : () -> T) {
var value : Any? = null
/**
* 对象第一次被调用时执行
*/
operator fun getValue(any: Any?, property: KProperty<*>): T {
println("getValue")
if (value == null) {
value = block()
}
return value as T
}
/**
* 被复制或对象属性发生变化时执行
*/
operator fun setValue(any1: Any?, property: KProperty<*>, any2: Any) {
println("setValue")
}
}
var p by Later<String> {
println("p 执行了")
"A"
}
p.toString()
kotlin已经为我们实现了懒加载机制:by lazy {}
private val uriMatcher by lazy {
val matcher = UriMatcher(UriMatcher.NO_MATCH)
matcher.addURI(authority, "book", bookDir)
matcher.addURI(authority, "book/#", bookItem)
matcher.addURI(authority, "category", categoryDir)
matcher.addURI(authority, "category/#", categoryItem)
matcher
}
by lazy 代码块是Kotlin 提供的一种懒加载技术,代码块中的代码一开始并不会执行,
只有当uriMatcher变量首次被调用的时候才会执行,
并且会将代码块中最后一行代码的返回值赋给uriMatcher。
默认情况下,对于 lazy 属性的求值是同步锁的(synchronized):该值只在一个线程中计算,并且所有线程会看到相同的值。
如果初始化委托的同步锁不是必需的,这样多个线程可以同时执行,那么将 LazyThreadSafetyMode.PUBLICATION 作为参数传递给 lazy() 函数。
而如果你确定初始化将总是发生在与属性使用位于相同的线程, 那么可以使用 LazyThreadSafetyMode.NONE 模式:它不会有任何线程安全的保证以及相关的开销。
可观察属性:
Delegates.observale()是实现可观察属性赋值后监听的方法;
相反的是有一个方法叫做Delegates.vetoable()是赋值前判断,满足条件才赋值。
class User {
var name: String by Delegates.observable("<no name>") {
property, old, new ->
println("$old -> $new")
}
var age: Int by Delegates.vetoable(0) { property, oldValue, newValue ->
//如果newValue大于30将不会赋值
newValue < 30
}
}
val user = User()
user.name = "first"
user.name = "second"
user.age = 20
println("age:" + user.age)
user.age = 40
println("age:" + user.age)
打印结果:
<no name> -> first
first -> second
age:20
age:20
将属性储存在映射中:
class User(val map: Map<String, Any?>) {
val name: String by map // 变量name必须和属性名称相同
val age: Int by map // 变量age必须和属性名称相同
}
val user = User(mapOf(
"name" to "John Doe",
"age" to 25
))
println(user.name)
println(user.age)
打印结果:
John Doe
25
[本章完...]
网友评论