构造函数
Kotlin的类包括1个主构造函数和多个次构造函数。
主构造函数
其中
主构造函数会紧跟类名进行声明。声明
主构造函数的关键字为constructor,主构造函数没有任何注解或者可见性修饰符,可省略该关键字。主构造函数不包括任何代码,所有构造函数会在init代码块执行,执行顺序按照从上到下执行。主构造函数中的参数,只能在属性初始化及init代码块中识别与执行,也可以在主构造函数中声明属性。-
可见性修饰符包括
public、protected、private、internal- 默认为
public -
private只在本文件中可见 -
internal只在本模块中可见 -
protected不能用于顶层声明
_ps: 模块的范围可以理解为是Modules,比如IntelliJ IDEA 模块、Maven 项目;Gradle 源集;
- 默认为
class Demo2Class public constructor(val name: String, age: Int) {
val firstProperty = "第一次初始化属性1:姓名: $name".also(::println)
init {
println("init代码块中可以获取到属性name:$name 和参数age:$age")
}
val secondProperty = "第二次初始化属性2: $age"
init {
println("secondProperty 初始化完成:$secondProperty")
println("init代码块与属性初始化按照顺序依次执行")
}
init{
print()
}
fun print(){
System.out.println("主构造函数中可以声明属性name:$name,和参数age(参数在fun中不可见)")
}
}
/**
输出如下:
第一次初始化属性1:姓名: liyao
init代码块中可以获取到属性name:liyao 和参数age:18
secondProperty 初始化完成:第二次初始化属性2: 18
init代码块与属性初始化按照顺序依次执行
主构造函数中可以声明属性name:liyao,和参数age(参数在fun中不可见)
*/
次构造函数
-
次级构造函数使用constructor关键字在类内部进行声明 -
次级构造函数同样需要指定注解或者可见型修饰符 -
次级构造函数要委托(继承)主构造函数,如果不包括主构造函数,则隐式委托默认主构造函数
constructor(name: String, age: Int, clazz: String) : this(name, age)
- 所有的
init块及属性初始化理论上都是主构造函数的body,主次关系,决定了先后顺序,在次级构造函数内代码执行之前,先执行初始化代码块,和代码物理顺序无关。 -
次级构造函数无法像主构造函数那样声明属性,参数也不能被初始化参数使用
继承
所有的对象都继承于Any类,提供 equals() 、 hashCode() 和 toString()
-
Any并不等同与Object,Any是Kotlin的类型,Object对于Kotlin来说,是平台类型
- Kotlin本身具备空安全的特性,但是由于Java的对象是都可以为null的,这就产生了矛盾
- 针对Java中声明的类型,Kotlin称作这些类型为
平台类型,会降低一部分类型的空安全要求,编译时不会报可控错误。如Object的子类。 - 同时,Kotlin 又特殊处理一部分 Java 类型进行了
类型映射,均是是非String的基础类型 -
Any不包括Object的wait()和notify()方法,如果希望使用Object类型,可以使用as关键字进行转型
(foo as java.lang.Object).wait()
-
类继承。默认情况下,所有Kotlin的类都是final类, 即不可被继承的类,如果需要继承,需要在类前添加open或abstract关键字。
ps:open标记可以理解为普通java类,abstract可以理解为抽象java类。这里可以看到kotlin的语法的衍进与安全性
open class Parent(p: Int){ }
...
class Children(p: Int) : Parent(p: Int){
}
-
方法重写-1。 默认情况下,open类的中的方法是不可以被override的, 如需override需要将方法标记为open,重写时,子类要在方法前标注为override
//in parent
open foo(p: Int) { }
//in children
override foo(p: Int) { }
-
方法重写-2。被override标记的方法(重写方法),是可以再被它的子类进行重写。如不希望其被重写,可以使用
final override foo(p: Int) { }
-
属性重写待补充
Kotlin中的接口
Kotlin的多继承问题
Kotlin和Java一样,支持
单继承(extends),多实现(implements)。在继承过程中,可能出现方法名的重复,可以只实现一个方法,再调用父类方法时可以通过
super<Parent>.foo()区分不同的实现
类属性
- 属性的完整定义包括
关键字、属性名、属性类型、初始化方法、getter、setter
var <propertyName>[: <PropertyType>] [= <property_initializer>]
[<getter>]
[<setter>]
kotlin 的
getter与setter中有可以访问到一个field属性,被称为backing property,接口中不支持backing property。可以简单理解为一个中间属性,协助完成getter与setter的赋值非空类型,如
String、Int的类型都是非空类型,定义初始化时要给予初始值。 如果要定义空类型一般需要在类型后面加?号,如String?、Int?。
class Demo2Class{
var test: String = "" // 非空类型,要初始化~
get() = "${field}3333" // !!! field 是backing property,这里不能直接访问this.test,会造成循环访问,stackoverflow
set(value) {
System.out.println("in setter")
System.out.println("value: $value")
field = "$value!" // !!! field 是backing property, 单独设置filed不会改变test的值,必须与getter进行联动
System.out.println("field $field")
}
}
val demo = Demo2Class()
demo.test = "123" // 触发setter
System.out.println("demo.test get()= ${demo.test}") // 触发getter
var是读写类型的关键字,如果要定义只读类型需要使用关键字val常量使用
const val修饰
延迟初始化
非空类型的属性必须在构造函数中被初始化(ps:属性定义也是构造函数的子过程),为了延迟初始化,可以使用lateinit关键词修饰,有两个限制:
- 只能修饰无自定义
setter与getter的属性 - 初始化前访问,会抛出异常
kotlin.UninitializedPropertyAccessException: lateinit property test2 has not been initialized - 安全起见(>_< 又是安全),可以通过
属性的引用的方法:.isInitialized,判断是否初始化,该方法只能使用在类内部
System.out.println("this::test2.isInitialized :${this::test2.isInitialized}") //属性的引用
