本文只记录重要的或者与C/C++、Java 出入较大的内容
当类没有结构体时可以省略大括号:class Test
主构造函数
class Test constructor(num :Int){
//...
}
class Test (num:Int){
//...
}
初始化代码块
class Test constructor(var num:Int){
init{
num = 5
println("num = $num")
}
}
如上,声明属性可以直接在类头声明
当构造函数不具有注释符或者使用默认的可见性修饰符时,可以省略constructor关键字
辅助构造函数
class Test{
constructor(参数){
}
}
同时存在主构造函数和二级构造函数
如果类具有主构造函数,则每个辅助构造函数需要通过另一个辅助构造函数直接或间接地委派给主构造函数。 使用this
关键字对同一类的另一个构造函数进行委派:
class Class_example2 constructor( num1:Int = 2){
private var num2 :Int = 30
init {
println(num1)
num2
}
constructor( num1:Int = 2, num3:Int):this(num1){
println("num3+num1 = $num1 + $num3")
}
//虽然这里写的是num3,但其实调用的是主构造函数
constructor(num1: Int = 2,num3: Int,num2:Int):this(num3){
println("constructor - 3 para")
}
}
fun main(){
var c :Class_example2 = Class_example2( 3,num3 = 3)
var cc:Class_example2 = Class_example2(1,2,3)
}
/*
输出:
3
num3+num1 = 3 + 3
2
constructor - 3 para
*/
当类的主构造函数都存在默认值的情况下
JVM
上,如果类主构造函数的所有参数都具有默认值,编译器将生成一个额外的无参数构造函数,它将使用默认值。 这使得更容易使用Kotlin
与诸如Jackson
或JPA
的库,通过无参数构造函数创建类实例。类的实例化
没有 new
关键字。
类的类别
密封类、内部类、抽象类、枚举类、接口类、数据类
Getter & Setter
修改访问器(Getter/Setter)的可见性
Setter
定义:如果属性使用至少一个访问器的默认实现,或者自定义访问通过field标识符引用,则将为属性生成后备字段。 (换句话,如果没有默认访问器实现 && 没有自定义通过访问field标识符进行引用,则不会有后备字段)
原理:Kotlin中没有字段,但有后备字段。在isEmpty例子中可以学习到,判断类中是否为空不需要单独的字段,只需要对size进行判断即可,因此该变量不需要字段。而size则需要后备字段。
class DummyClass {
var size = 0;
var isEmpty //no backing field
get() = size == 0
set(value) {
size = size * 2
}
}
public final class DummyClass {
private int size;
public final int getSize() {
return this.size;
}
public final void setSize(int var1) {
this.size = var1;
}
public final boolean isEmpty() {
return this.size == 0;
}
public final void setEmpty(boolean value) {
this.size *= 2;
}
}
后备属性
_table是private 没有法访问,定义了table属性来对 _table进行get操作。
private var _table: Map<String, Int>? = null
public val table: Map<String, Int> /// table就是后备属性。
get() {
if (_table == null) {
_table = HashMap() // 初始化
}
// ?: 操作符,如果_table不为空则返回,反之则抛出AssertionError异常
return _table ?: throw AssertionError("Set to null by another thread")
}
编译时常数
编译时常数必须为顶层声明,初始化为String
或者基本类型,没有自定义的getter()
const val CONST_NUM = 5
const val CONST_STR = "Kotlin"
后期初始化属性
只能用于修饰var,没有自定义的setter与getter函数,属性必须为空且类型不能为基本类型。
委托属性
public、internal、protected、private
顶层声明
在类中声明
在接口中声明
在构造函数中的声明
在局部声明中同上步中的二级构造函数
与Java中的对比
四个修饰符不同、默认修饰符不同
超类(Any);用:符号继承
open修饰符
继承类的构造函数
实现类无主构造函数
每个辅助构造函数必须使用super
关键字初始化或者委托给另一个构造函数。
存在主构造函数
主构造函数一般实现基类中参数最多的构造函数,参数少的哦那个this引用即可。
函数的重写
子类不能重写基类中没有用open修饰的同名函数。
当一个类不是用open修饰时,该类默认实final,不能被再次继承
子类用final关键字修饰方法,以此来禁止后续子类重写该方法。
open class A{
open fun foo(){}
}
// B这个类继承类A,并且类B同样使用open修饰符修饰了的
open class B : Demo(){
// 这里使用final修饰符修饰该方法,禁止覆盖掉类A的foo()函数
final override fun foo(){}
}
重写属性
重写属性必须用override修饰。
当基类属性修饰为val时,实现类可以用var去重写,反之却不行。
open class Demo{
open val valStr = "我是用val修饰的属性"
}
class DemoTest : Demo(){
/*
* 这里用val、或者var重写都是可以的。
* 不过当用val修饰的时候不能有setter()函数,编辑器直接会报红的
*/
// override val valStr: String
// get() = super.valStr
// override var valStr: String = ""
// get() = super.valStr
// override val valStr: String = ""
override var valStr: String = "abc"
set(value){field = value}
}
fun main(arge: Array<String>>){
println(DemoTest().valStr)
val demo = DemoTest()
demo.valStr = "1212121212"
println(demo.valStr)
}
重写属性是不能用 get() = super.xxx,因为这样的话,不管你是否重新为该属性赋了新值,还是支持setter()
,在使用的时候都调用的是基类中的属性值。
class DemoTest : Demo(){
/*
* 这里介绍重写属性是,getter()函数中使用`super`关键字的情况
*/
override var valStr: String = "abc"、
get() = super.valStr
set(value){field = value}
}
fun main(arge: Array<String>>){
println(DemoTest().valStr)
val demo = DemoTest()
demo.valStr = "1212121212"
println(demo.valStr)
}
也不能 get() = this.valStr / get() = valStr 。会报运行错误。
Exception:StackOverflowError
java.lang.StackOverflowError:stacksize8MBStackOverflowError是由于当前线程的栈满了,也就是函数调用层级过多导致。堆栈溢出错误一般是递归调用。出现这种异常,大多是由于循环调用。出现的情况:大多数都是在本方法中调用本方法。也就是我们常说的递归调用,所以才导致这个错误的出现。
应该用默认的 get() = field
在主构造函数重写
class DemoTest2(override var num: Int, override val valStr: String) : Demo()
fun main(args: Array<String>){
val demo2 = DemoTest2(1,"构造函数中重写")
println("num = ${demo2.num} \t valStr = ${demo2.valStr}")
}
覆盖规则:解决两个接口方法名相同问题
open class A{
open fun test1(){ println("基类A中的函数test1()") }
open fun test2(){println("基类A中的函数test2()")}
}
interface B{
fun test1(){ println("接口类B中的函数test1()") }
fun test2(){println("接口类B中的函数test2()")}
}
class C : A(),B{
override fun test1() {
super<A>.test1()
super<B>.test1()
}
override fun test2() {
super<A>.test2()
super<B>.test2()
}
}
枚举类的初始化及使用
enum class Color(var argb : String){
RED(""),
WHITE(""),
BLACK(""),
GREEN("")
}
枚举常量,枚举类中的每个枚举常量都是对象,用逗号分隔。(如上述的RED(“”),)
直接用Color.RED
进行访问。
枚举常量匿名类,必须提供一个抽象方法,且该方法定义在枚举类内部。而且必须在枚举变量的后面,有抽象函数,则最后一个枚举变量必须用;
隔开。
枚举常量的属性:name(常量名)和ordinal(常量位置)
可以用enumValues<T>()
和enumValuesOf<T>()
访问
println(enumValues<Color>().joinToString { it.name })
println(enumValueOf<Color>("RED"))
//输出
//RED, WHITE, BLACK, GREEN
//RED
用 valueof()
和values()
检测
println(Color.valueOf("RED"))
println(Color.values()[0])
println(Color.values()[1])
println(Color.values()[2])
println(Color.values()[3])
其中,若使用Color.valueOf("不存在的枚举常量")
,则会抛出IllegalArgumentException
异常,即枚举变量不存在。若使用Color.values()[大于枚举常量位置]
,则会抛出下标越界异常。
:
进行对接口的实现super<接口名>.方法名
来区分。data
sealed
abstract
关键字public
定义:一个类嵌套在另一个类当中
外部类.嵌套类().嵌套类方法/属性。在调用的时候嵌套类是需要实例化的
。
class Outter{
class Nested{
fun execute(){
Log.d("test", "Nested -> execute")
}
}
}
// 调用
Outter.Nested().execute()
//输出
Nested -> execute
定义:用inner class 来进行声明类
内部类不能直接被实例化,需要外部的类实例化了对象,再利用该对象进行实例化内部类。
class Outter{
val testVal = "test"
inner class Inner{
fun execute(){
Log.d("test", "Inner -> execute : can read testVal=$testVal")
}
}
}
// 调用
val outter = Outter()
outter.Inner().execute()
// 输出
Inner -> execute : can read testVal=test
监听器的实现方法
package edu.zju.maple.learning
class NickInnerClass{
lateinit private var listener:OnClickListener
fun setOnClickListener(listener: OnClickListener){
this.listener = listener
}
fun activeListener(){
listener.onItemClick()
}
}
interface OnClickListener{
fun onItemClick()
}
fun main(){
val nick = NickInnerClass()
nick.setOnClickListener(object:OnClickListener{
override fun onItemClick() {
println("执行了activeListener函数,才有这句输出")
}
})
var i=0;
while (i<20){
i++
nick.activeListener() ///唤醒方法
for ( temp in 1..0xFFFF){
}
}
}