1、类
关键字也是class,和java不同的是它的构造函数直接在类声明时就声明了,小括号中就是构造函数的参数列表
class Person constructor(firstName: String) {} //当主构造器没有注解或可见性修饰时可以省略关键字constructor
2、初始化代码块
由于没有像java那样显式个构造方法,所以初始化操作就在init方法中进行
class Person{
init{
print("在这里做一些初始化操作")
}
}
3、构造函数
类默认拥有一个无参构造,如果有了其他的构造,那么默认的就没了。
次构造器:
class Person(name:String){
constructor(name:String,age:Int):this(name){}
}
次构造器要委托(继承)主构造器 :this(arg)
如果使用父类的构造器就用super代替this
super\
class A:B,C{
super<B>.name = "B的"
super<C>.name = "C的"
}
4、可见性
默认就是是public可见的,peivate是不可见的
5、属性
属性的可见性、默认setter、getter方法这是和scala一样的
不同点:成员属性在scala中可以先声明后初始化。而在kotlin中必须声明并初始化
自定义setter、getter方法不同。如:
class Person(){
var name:String = "张三" //此处就是为幕后字段field赋值
get() = field
set(value){ field = value}
var isEmpty:Boolean = false //成员变量必须要初始化,不管用没用到field
get()=xxxx
val isEmpty:Boolean //成员常量没有用到field就不需要初始化
get()=true
val isEmpty:Boolean = false //成员常量用到field就要初始化
get()=field
}
类的可见属性默认是有getset方法的。如果我们自己不满足系统提供给我们的setget方法时,我们就可以自己去重写。标准格式如上,其中有一个幕后字段field。
它就代表该属性的原始值,可以理解成堆中的值。还可以在setget方法前加private进行私有化
如果是常量val,就不能有set方法
6、编译期常量
在java中就是一个类的静态常量public static final xxx
在kotlin中就是const val xxx
- 注意:在kotlin中的编译期常量必须满足
- 位于顶层 或者 是 object 的一个成员 //顶层的意思就是在类的外部
- 用 String 或原生类型 值初始化
- 没有自定义 getter
如:创建一个文件Constant.kt
const val version = 1
object Constant{
const val versionName="1.0.0" //这个可以不写const
}
在其他文件中访问
print(version)
//或
print(Constant.varsionName)
7、继承
在java中类默认是可以被继承的,不允许被继承就用fina来修饰。但是在kotlin中刚好相反,类默认是不被继承的,也就是说是默认就额比final修饰的,要想可以被继承,就用open修饰类的继承是继承父类的主构造器,而不是类名。
8、重写(覆盖)
override关键字来重载变量或方法的。当然,变量和方法默认也是final的,不能被重写的,所以要想被重写就必须要用open来修饰才能被子类重写。
一个val可以被覆盖成var,因为val只有getter,覆盖成var就是加一个setter的事,但是反之就不行
重写父类的属性既可以在子类的构造器参数中override,也可以在类中去override,只要属性是open的
operator关键字用来重载操作符和运算符的。
如:重解构中的componentN方法就需要这个关键字修饰
属性委托中的代理对象的setValue和getValue方法也需要这个关键字
9、抽象类
抽象类可以被继承,不用open修饰。而类中的属性还是要open修饰后才能重写的
10、接口interface
和scala一样接口中的方法可以实现也可以不实现
属性可以写getset方法,但是不能调用field幕后字段,因为压根没有幕后字段。也不能初始化赋值
所有的属性和方法可以直接override
实现接口同样用冒号“:”。多实现有逗号隔开
11、可见性
public默认的、protected、private、internal这个是只在同一个moudel下可见
12、扩展
class Man(val name:String){}
object Test{
val man = Man("张三")
man.fly() //Man中没有fly这个方法,那么我们就对Man进行扩展
}
fun Man.fly(){//扩展Man类。添加一个fly方法
println(this.name+"can fly") //此处this代表的就是被扩展类的对象。打印就是张三can fly
}
类的扩展是静态解析,并没有真正的在类中添加成员,所以调用的时候根据形参来决定调用对象如:
open class A
class B:A{
fun A.haha()="a"
fun B.haha()="b"
fun show(a:A){
print(a.haha()) //这里决定调谁的方法
}
show(A()) //打印是a
show(B()) //打印的还是a,因为有形参来决定了
如果原类中已有某方法,那么再扩展就没有用,当然不同参数就有用
扩展属性
和扩展方法一样,把fun换成var或val即可。注意,由于扩展并未将成员插入类中,所以不能访问幕后字段,所以也不能有初始化器
class B
class A{
fun B.haha(){
toString() //B的tostring
this@A.toString() //A的tostring
}
}
13、数据类
可以理解成java中的bean类来使用。在类之前加data来修饰
主构造函数需要至少有一个参数;
主构造函数的所有参数需要标记为 val 或 var;
数据类不能是抽象、开放、密封或者内部的;
会创建copy方法,可以传某个具体参数,只改变该参数对应的属性,其他属性不变,复制出一个对象如:
data class A(val name:String,val age:Int)
val a = A(age = 1,name = "张三")
println(a.name+":"+a.age) //张三:1
val copy = a.copy(age = 12)
println(copy.name+":"+copy.age) //张三:12
//还会创建componentN()方法来解构类,按照类中变量的声明顺序来解构
val name = a.component1()
val age = a.component2()
//还有toString方法和hashCode和equals方法都已经重写好了
14、密封类
该类的子类是有限的。用sealed 关键字来修饰class
15、枚举
该类的成员时有限的。用enum 来修饰class
enum class Season{ SPRING,SUMMER,AUTUMN,WINTER}
16、对象表达式
完全可以理解成java中的匿名内部类,只是要用object来接受
button.setOnClickListener(object:ClickLisener(),otherClass{
override fun onClick(view:View){
//todo
}
})
还有厉害的地方,他可以继续继承或实现其他接口(用逗号隔开再继承其他类otherClass),这是java中的匿名内部类所达不到的
17、单例对象、静态对象
用object来声明的类就是单例对象、静态对象,直接调用里面的变量和方法。用类名点属性名和方法名
18、伴生对象
这个伴生对象和scala的伴生对象写法有区别
class MyClass {
companion object objName{
fun create(): MyClass = MyClass()
}
}
伴生对象要写在类中,要用companion和object两个关键字
伴生对象名可以省略
获取该伴生对象val x = MyClass.Companion ,要大写
使用伴生对象中的方法可以用x来调也可以用MyClass来调
19、委托属性
属性委托和类委托的思想是一致的,类的委托是让代理类为委托类做事,同样这里是代理属性替委托属性做事,其实就是代理属性的set和get方法替委托属性的set和get方法做事。
class Man{
var name:String by People()
}
class People{
var name:String="男人"//这个自己定义一个,我不知道是不是这么用,反正能表达出意思
operator fun getValue(man: Man, property: KProperty<*>): String {
return name
}
operator fun setValue(man: Man, property: KProperty<*>, s: String) {
name = s
}
}
fun main(args: Array<String>) {
val man = Man()
println(man.name)//打印男人
man.name = "张三"
println(man.name)//打印张三
}
就是调用man的get方法走的是people的getValue方法,调用man的set方法走的是people的setValue方法。完全呗people代理了man的name属性
20、标准委托
- 懒加载lazy
val lazyValue: String by lazy { "Hello" } fun main(args: Array<String>) { println(lazyValue)//lazyValue原本是没有值的,只有在真正使用的地方才会为其初始化,这就是懒加载。也是一种属性委托 }
- 监听、判断
class Person{ var name:String by Delegates.observable("张三",{property,oldValue,newValue-> println(newValue)})//张三是初始值,当重新赋值时会触发lambda表达式,先赋值后触发,值已经改变了。 var age:Int by Delegates.vetoable(1,{p,old,new->new >0}) //当重新赋值时会先判断,如果lambda表达式为true,才会将新值赋进去,否则就是初始值1 } fun main(args:Array<String>){ val p = Person() p.name = "李四"//重新赋值 p.age=18 }
- 非空初始化
作为全局变量要先初始化,但我们还不知道该为他初始化为何值,但又不想让他先为空,因为那样后面操作时都要非空判断,所以就先用这种委托初始化,当真正赋值的地方才为其赋值,如还没真正赋值就调用,就会报异常。类似与空指针var tv:TextView by Delegates.notNull<TextView>()
- 从map中映射
可以把map中的全部值映射成一个对象class Configuration(map: Map<String, Any?>) { val width: Int by map val height: Int by map val dp: Int by map val deviceName: String by map }
21、自定义委托
class MyDalegate {
operator fun getValue(student: Student, property: KProperty<*>): String {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
operator fun setValue(student: Student, property: KProperty<*>, s: String) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}
class MyDelegate2 : ReadWriteProperty<Any,Int> {
override fun getValue(thisRef: Any, property: KProperty<*>): Int {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun setValue(thisRef: Any, property: KProperty<*>, value: Int) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}
class MyDelegate3 :ReadOnlyProperty<Any,String> {
override fun getValue(thisRef: Any, property: KProperty<*>): String {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}