构造函数
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}") //属性的引用