Kotlin 基础语法
Kotlin 文件以 .kt 为后缀。
函数定义
函数定义使用关键字 fun,参数格式为:参数 : 类型
1 | fun sum(a: Int, b: Int): Int { // Int 参数,返回值 Int |
表达式作为函数体,返回类型自动推断:
1 | fun sum(a: Int, b: Int) = a + b |
无返回值的函数(类似Java中的void):
1 | fun printSum(a: Int, b: Int): Unit { |
可变长参数函数
函数的变长参数可以用 vararg 关键字进行标识:
1 | fun vars(vararg v:Int){ |
lambda(匿名函数)
lambda表达式使用实例:
1 | // 测试 |
NULL检查机制
Kotlin的空安全设计对于声明可为空的参数,在使用时要进行空判断处理,有两种处理方式,字段后加!!像Java一样抛出空异常,另一种字段后加?可不做处理返回值为 null或配合?:做空判断处理
1 | //类型后面加?表示可为空 |
当一个引用可能为 null 值时, 对应的类型声明必须明确地标记为可为 null。
当 str 中的字符串内容不是一个整数时, 返回 null:
1 | fun parseInt(str: String): Int? { |
Kotlin 基本数据类型
Kotlin 的基本数值类型包括 Byte、Short、Int、Long、Float、Double 等。不同于 Java 的是,字符不属于数值类型,是一个独立的数据类型。
Kotlin 中没有基础数据类型,只有封装的数字类型。你每定义的一个变量,其实 Kotlin 帮你封装了一个对象,这样可以保证不会出现空指针。数字类型也一样,所以在比较两个数字的时候,就有比较数据大小和比较两个对象是否相同的区别了。
在 Kotlin 中,三个等号 === 表示比较对象地址,两个 == 表示比较两个值大小。
var x = 1..16
: [1,16]var x = 1 until 16
: [1, 16)
变量和常量
可变变量定义:var 关键字
1 | var <标识符> : <类型> = <初始化值> |
字符串模板
$
表示一个变量名或者变量值$varName
表示变量值${varName.fun()}
表示变量的方法返回值:
1 | var a = 1// 模板中的简单名称:val s1 = "a is $a" a = 2// 模板中的任意表达式:val s2 = "${s1.replace("is", "was")}, but now is $a" |
Kotlin 条件控制
Kotlin 循环控制
1 | // 对任何提供迭代器(iterator)的**对象**进行遍历for (item in collection) print(item)// 通过**索引**遍历一个数组或者一个 listfor (i in array.indices) { print(array[i])}// 索引+值,相当于Python中的enumeratefor ((index, value) in array.withIndex()) { println("the element at $index is $value")} |
1 | // while循环while( 布尔表达式 ) { //循环内容}// do循环do { //代码语句}while(布尔表达式); |
返回和跳转
Kotlin 有三种结构化跳转表达式:
- return。默认从最直接包围它的函数或者匿名函数返回。
- break。终止最直接包围它的循环。
- continue。继续下一次最直接包围它的循环。
Break 和 Continue 的跳转标签
在 Kotlin 中任何表达式都可以用标签(label)来标记。 标签的格式为标识符+跟 @
,例如:abc@、fooBar@都是有效的标签。 要为一个表达式加标签,我们只要在其前加标签即可
1 | loop@ for (i in 1..100) { for (j in 1..100) { if (……) break@loop }} |
标签限制使得 break 跳转到刚好位于该标签指定循环的后面执行点。 continue 继续标签指定的循环的下一次迭代。
标签处返回
Kotlin 有函数字面量、局部函数和对象表达式。因此 Kotlin 的函数可以被嵌套。 标签限制的 return 允许我们从外层函数返回。 标签处返回的最重要的一个用途就是从 lambda 表达式中返回。回想一下我们这么写的时候:
1 | fun foo() { ints.forEach { if (it == 0) return print(it) }} |
这个 return 表达式从最直接包围它的函数即 foo 中返回。 (注意,这种非局部的返回只支持传给内联函数的 lambda 表达式。) 如果我们需要从 lambda 表达式中返回,我们必须给它加标签并用以限制 return。
1 | fun foo() { ints.forEach lit@ { if (it == 0) return@lit print(it) }} |
现在,它只会从 lambda 表达式中返回。通常情况下使用隐式标签更方便。 该标签与接受该 lambda 的函数同名。
1 | fun foo() { ints.forEach { if (it == 0) return@forEach print(it) }} |
或者,我们用一个匿名函数替代 lambda 表达式。 匿名函数内部的 return 语句将从该匿名函数自身返回
1 | fun foo() { ints.forEach(fun(value: Int) { if (value == 0) return print(value) })} |
当要返一个回值的时候,解析器优先选用标签限制的 return,即
1 | return@a 1 |
意为"从标签 @a 返回 1",而不是"返回一个标签标注的表达式 (@a 1)"。
Kotlin 类和对象
类的定义
1 | class Runoob constructor(name: String) { fun foo() { print("Foo") } // 成员函数 var url: String = …… // 次构造函数 constructor (name: String, alexa: Int) : this(name) { println("Alexa 排名 $alexa") } var lastName: String = "zhang" get() = field.toUpperCase() // 将变量赋值后转换为大写 set var no: Int = 100 get() = field // 后端变量 set(value) { if (value < 10) { // 如果传入的值小于 10 返回该值 field = value } else { field = -1 // 如果传入的值大于等于 10 返回 -1 } } var heiht: Float = 145.4f private set } |
对象的声明
1 | val site = Runoob() // Kotlin 中没有 new 关键字site.name // 使用 . 号来引用site.url |
构造函数
Koltin 中的类可以有一个 主构造器,以及一个或多个次构造器,主构造器是类头部的一部分,位于类名称之后:
1 | class Person constructor(firstName: String) { init { println("FirstName is $firstName") }} |
如果主构造器没有任何注解,也没有任何可见度修饰符,那么constructor关键字可以省略。
1 | class Person(firstName: String) {} |
次构造函数
类也可以有二级构造函数,需要加前缀 constructor:
1 | class Person { constructor(parent: Person) { parent.children.add(this) }} |
如果类有主构造函数,每个次构造函数都要,或直接或间接通过另一个次构造函数代理主构造函数。在同一个类中代理另一个构造函数使用 this 关键字:
1 | class Person(val name: String) { constructor (name: String, age:Int) : this(name) { // 初始化... }} |
如果一个非抽象类没有声明构造函数(主构造函数或次构造函数),它会产生一个没有参数的构造函数。构造函数是 public 。如果你不想你的类有公共的构造函数,你就得声明一个空的主构造函数:
1 | class DontCreateMe private constructor () {} |
getter 和 setter
1 | var <propertyName>[: <PropertyType>] [= <property_initializer>] [<getter>] [<setter>]// var变量可设置getter 和 setter, 都是可选// 如果属性类型可以从初始化语句或者类的成员函数中推断出来,那就可以省去类型,val不允许设置setter函数,因为它是只读的。var allByDefault: Int? // 错误: 需要一个初始化语句, 默认实现了 getter 和 setter 方法var initialized = 1 // 类型为 Int, 默认实现了 getter 和 setterval simple: Int? // 类型为 Int ,默认实现 getter ,但必须在构造函数中初始化val inferredType = 1 // 类型为 Int 类型,默认实现 getter |
抽象类
抽象是面向对象编程的特征之一,类本身,或类中的部分成员,都可以声明为abstract的。抽象成员在类中不存在具体的实现。
注意:无需对抽象类或抽象成员标注open注解。
1 | open class Base { open fun f() {}}abstract class Derived : Base() { override abstract fun f()} |
嵌套类
我们可以把类嵌套在其他类中,看以下实例:
1 | class Outer { // 外部类 private val bar: Int = 1 class Nested { // 嵌套类 fun foo() = 2 }} |
内部类
内部类使用 inner 关键字来表示。
内部类会带有一个对外部类的对象的引用,所以内部类可以访问外部类成员属性和成员函数。
1 | class Outer { private val bar: Int = 1 var v = "成员属性" /**嵌套内部类**/ inner class Inner { fun foo() = bar // 访问外部类成员 fun innerTest() { var o = this@Outer //获取外部类的成员变量 println("内部类可以引用外部类的成员,例如:" + o.v) } }} |
匿名内部类
使用对象表达式来创建匿名内部类:
1 | class Test { var v = "成员属性" fun setInterFace(test: TestInterFace) { test.test() }}/** * 定义接口 */interface TestInterFace { fun test()}fun main(args: Array<String>) { var test = Test() /** * 采用对象表达式来创建接口对象,即匿名内部类的实例。 */ test.setInterFace(object : TestInterFace { override fun test() { println("对象表达式创建匿名内部类的实例") } })} |
类的修饰符
类的修饰符包括 classModifier 和_accessModifier_:
-
classModifier: 类属性修饰符,标示类本身特性。
1
abstract // 抽象类 final // 类不可继承,默认属性enum // 枚举类open // 类可继承,类默认是final的annotation // 注解类
-
accessModifier: 访问权限修饰符
1
private // 仅在同一个文件中可见protected // 同一个文件中或子类可见public // 所有调用的地方都可见internal // 同一个模块中可见
Kotlin 继承
Kotlin 中所有类都继承该 Any 类,它是所有类的超类,对于没有超类型声明的类是默认超类:
Any 默认提供了三个函数:
- equals()
- hashCode()
- toString()
注意:Any 不是 java.lang.Object。
如果一个类要被继承,可以使用 open 关键字进行修饰。
1 | open class Base(p: Int) // 定义基类class Derived(p: Int) : Base(p) |
构造函数
子类有主构造函数
如果子类有主构造函数, 则基类必须在主构造函数中立即初始化。
1 | open class Person(var name : String, var age : Int){// 基类}class Student(name : String, age : Int, var no : String, var score : Int) : Person(name, age) {}// 测试fun main(args: Array<String>) { val s = Student("Runoob", 18, "S12346", 89) println("学生名: ${s.name}") println("年龄: ${s.age}") println("学生号: ${s.no}") println("成绩: ${s.score}")} |
输出结果:
1 | 学生名: Runoob年龄: 18学生号: S12346成绩: 89 |
子类没有主构造函数
如果子类没有主构造函数,则必须在每一个二级构造函数中用 super 关键字初始化基类,或者在代理另一个构造函数。初始化基类时,可以调用基类的不同构造方法。
1 | class Student : Person { constructor(ctx: Context) : super(ctx) { } constructor(ctx: Context, attrs: AttributeSet) : super(ctx,attrs) { }} |
重写
在基类中,使用fun声明函数时,此函数默认为final修饰,不能被子类重写。如果允许子类重写该函数,那么就要手动添加 open 修饰它, 子类重写方法使用 override 关键词:
1 | /**用户基类**/open class Person{ open fun study(){ // 允许子类重写 println("我毕业了") }}/**子类继承 Person 类**/class Student : Person() { override fun study(){ // 重写方法 println("我在读大学") }}fun main(args: Array<String>) { val s = Student() s.study();} |
如果有多个相同的方法(继承或者实现自其他类,如A、B类),则必须要重写该方法,使用super范型去选择性地调用父类的实现。
1 | open class A { open fun f () { print("A") } fun a() { print("a") }}interface B { fun f() { print("B") } //接口的成员变量默认是 open 的 fun b() { print("b") }}class C() : A() , B{ override fun f() { super<A>.f()//调用 A.f() super<B>.f()//调用 B.f() }}fun main(args: Array<String>) { val c = C() c.f();} |
C 继承自 a() 或 b(), C 不仅可以从 A 或则 B 中继承函数,而且 C 可以继承 A()、B() 中共有的函数。此时该函数在中只有一个实现,为了消除歧义,该函数必须调用A()和B()中该函数的实现,并提供自己的实现。
输出结果为:
1 | AB |
属性重写
属性重写使用 override 关键字,属性必须具有兼容类型,每一个声明的属性都可以通过初始化程序或者getter方法被重写:
1 | open class Foo { open val x: Int get { …… }}class Bar1 : Foo() { override val x: Int = ……} |
注:可以用一个var属性重写一个val属性,但是反过来不行。
Kotlin 接口
接口定义
Kotlin 接口与 Java 8 类似,使用 interface 关键字定义接口,允许方法有默认实现:
1 | interface MyInterface { fun bar() // 未实现 fun foo() { //已实现 // 可选的方法体 println("foo") } // 接口中的属性只能是抽象的,不允许初始化值,接口不会保存属性值,实现接口时,必须重写属性: var name:String //name 属性, 抽象的} |
实现接口
一个类或者对象可以实现一个或多个接口。
1 | class Child : MyInterface { override fun bar() { // 方法体 }} |
Kotlin 扩展
Kotlin 可以对一个类的属性和方法进行扩展,且不需要继承或使用 Decorator 模式。
扩展是一种静态行为,对被扩展的类代码本身不会造成任何影响。
将函数扩展
扩展函数可以在已有类中添加新的方法,不会对原类做修改,扩展函数定义形式:
- receiverType:表示函数的接收者,也就是函数扩展的对象
- functionName:扩展函数的名称
- params:扩展函数的参数,可以为NULL
1 | // 函数扩展原型fun receiverType.functionName(params){ body}class User(var name:String)/**扩展函数**/fun User.Print(){ print("用户名 $name")} |
扩展函数是静态解析的
扩展函数是静态解析的,并不是接收者类型的虚拟成员,在调用扩展函数时,具体被调用的的是哪一个函数,由调用函数的的对象表达式来决定的,而不是动态的类型决定的:
1 | open class Cclass D: C()fun C.foo() = "c" // 扩展函数 foofun D.foo() = "d" // 扩展函数 foofun printFoo(c: C) { println(c.foo()) // 类型是 C 类}fun main(arg:Array<String>){ printFoo(D())}// 输出c |
若扩展函数和成员函数一致,则使用该函数时,会优先使用成员函数。
扩展一个空对象
在扩展函数内, 可以通过 this 来判断接收者是否为 NULL,这样,即使接收者为 NULL,也可以调用扩展函数。例如:
1 | fun Any?.toString(): String { if (this == null) return "null" // 空检测之后,“this”会自动转换为非空类型,所以下面的 toString() // 解析为 Any 类的成员函数 return toString()}fun main(arg:Array<String>){ var t = null println(t.toString())} |
实例执行输出结果为:
1 | null |
扩展属性允许定义在类或者kotlin文件中,不允许定义在函数中。初始化属性因为属性没有后端字段(backing field),所以不允许被初始化,只能由显式提供的 getter/setter 定义。
1 | val Foo.bar = 1 // 错误:扩展属性不能有初始化器 |
扩展属性只能被声明为 val。
伴生对象的扩展
伴生对象内的成员相当于 Java 中的静态成员,其生命周期伴随类始终,在伴生对象内部可以定义变量和函数,这些变量和函数可以直接用类名引用。
如果一个类定义有一个伴生对象 ,你也可以为伴生对象定义扩展函数和属性。
伴生对象通过"类名."形式调用伴生对象,伴生对象声明的扩展函数,通过用类名限定符来调用:
1 | class MyClass { companion object { } // 将被称为 "Companion"}fun MyClass.Companion.foo() { println("伴随对象的扩展函数")}val MyClass.Companion.no: Int get() = 10fun main(args: Array<String>) { println("no:${MyClass.no}") MyClass.foo()} |
实例执行输出结果为:
1 | no:10伴随对象的扩展函数 |
注:对于伴生对象扩展函数,有两种形式,一种是在类内扩展,一种是在类外扩展,这两种形式扩展后的函数互不影响(甚至名称都可以相同),即使名称相同,它们也完全是两个不同的函数,并且有以下特点:
- (1)类内扩展的伴随对象函数和类外扩展的伴随对象可以同名,它们是两个独立的函数,互不影响;
- (2)当类内扩展的伴随对象函数和类外扩展的伴随对象同名时,类内的其它函数优先引用类内扩展的伴随对象函数,即对于类内其它成员函数来说,类内扩展屏蔽类外扩展;
- (3)类内扩展的伴随对象函数只能被类内的函数引用,不能被类外的函数和伴随对象内的函数引用;
- (4)类外扩展的伴随对象函数可以被伴随对象内的函数引用,;
Kotlin 对象表达式和对象声明
对象表达式
通过对象表达式实现一个匿名内部类的对象用于方法的参数中:
1 | window.addMouseListener(object : MouseAdapter() { override fun mouseClicked(e: MouseEvent) { // ... } override fun mouseEntered(e: MouseEvent) { // ... }}) |
对象声明
Kotlin 使用 object 关键字来声明一个对象。
Kotlin 中我们可以方便的通过对象object声明来获得一个单例。
当对象声明在另一个类的内部时,这个对象并不能通过外部类的实例访问到该对象,而只能通过类名来访问,同样该对象也不能直接访问到外部类的方法和变量。
1 | class Site { var name = "菜鸟教程" object DeskTop{ var url = "www.runoob.com" fun showName(){ print{"desk legs $name"} // 错误,不能访问到外部类的方法和变量 } }}fun main(args: Array<String>) { var site = Site() site.DeskTop.url // 错误,不能通过外部类的实例访问到该对象 Site.DeskTop.url // 正确, 类似静态内部类,这个类是属于外部类的,而不是某个实例} |
伴生对象
类内部的对象声明可以用 companion
关键字标记,这样它就与外部类关联在一起,我们就可以直接通过外部类访问到对象的内部元素。
对象表达式和对象声明之间的语义差异
对象表达式和对象声明之间有一个重要的语义差别:
- 对象表达式是在使用他们的地方立即执行的
- 对象声明是在第一次被访问到时延迟初始化的
- 伴生对象的初始化是在相应的类被加载(解析)时,与 Java 静态初始化器的语义相匹配
kotlin 委托
委托模式是软件设计模式中的一项基本技巧。在委托模式中,有两个对象参与处理同一个请求,接受请求的对象将请求委托给另一个对象来处理。
Kotlin 直接支持委托模式,更加优雅,简洁。Kotlin 通过关键字 by 实现委托。
类委托
1 | // 创建接口interface Base { fun print()}// 实现此接口的被委托的类class BaseImpl(val x: Int) : Base { override fun print() { print(x) }}// 通过关键字 by 建立委托类class Derived(b: Base) : Base by bfun main(args: Array<String>) { val b = BaseImpl(10) Derived(b).print() // 输出 10} |
在 Derived 声明中,by 子句表示,将 b 保存在 Derived 的对象实例内部,而且编译器将会生成继承自 Base 接口的所有方法, 并将调用转发给 b。
属性委托
属性委托指的是一个类的某个属性值不是在类中直接进行定义,而是将其托付给一个代理类,从而实现对该类的属性统一管理。
1 | import kotlin.reflect.KProperty// 定义包含属性委托的类class Example { var p: String by Delegate() // val/var <属性名>: <类型> by <表达式>}// 委托的类class Delegate { operator fun getValue(thisRef: Any?, property: KProperty<*>): String { return "$thisRef, 这里委托了 ${property.name} 属性" } operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) { println("$thisRef 的 ${property.name} 属性赋值为 $value") }}fun main(args: Array<String>) { val e = Example() println(e.p) // 访问该属性,调用 getValue() 函数 e.p = "Runoob" // 调用 setValue() 函数 println(e.p)} |
标准委托
Kotlin 的标准库中已经内置了很多工厂方法来实现属性的委托。
延迟属性 Lazy
1 | val lazyValue: String by lazy { println("computed!") // 第一次调用输出,第二次调用不执行 "Hello"}fun main(args: Array<String>) { println(lazyValue) // 第一次执行,执行两次输出表达式 println(lazyValue) // 第二次执行,只输出返回值}/*computed!HelloHello*/ |
可观察属性 Observable
observable 可以用于实现观察者模式。
Delegates.observable() 函数接受两个参数: 第一个是初始化值, 第二个是属性值变化事件的响应器(handler)。
在属性赋值后会执行事件的响应器(handler),它有三个参数:被赋值的属性、旧值和新值:
1 | import kotlin.properties.Delegatesclass User { var name: String by Delegates.observable("初始值") { prop, old, new -> println("旧值:$old -> 新值:$new") }}fun main(args: Array<String>) { val user = User() user.name = "第一次赋值" user.name = "第二次赋值"}/*旧值:初始值 -> 新值:第一次赋值旧值:第一次赋值 -> 新值:第二次赋值*/ |
把属性储存在映射中
Not Null
notNull 适用于那些无法在初始化阶段就确定属性值的场合。
1 | class Foo { var notNullBar: String by Delegates.notNull<String>()}foo.notNullBar = "bar"println(foo.notNullBar) |
需要注意,如果属性在赋值前就被访问的话则会抛出异常。
局部委托属性
你可以将局部变量声明为委托属性。 例如,你可以使一个局部变量惰性初始化:
1 | fun example(computeFoo: () -> Foo) { val memoizedFoo by lazy(computeFoo) if (someCondition && memoizedFoo.isValid()) { memoizedFoo.doSomething() }} |
memoizedFoo 变量只会在第一次访问时计算。 如果 someCondition 失败,那么该变量根本不会计算。
属性委托要求
对于只读属性(也就是说val属性), 它的委托必须提供一个名为getValue()的函数。该函数接受以下参数:
- thisRef —— 必须与属性所有者类型(对于扩展属性——指被扩展的类型)相同或者是它的超类型
- property —— 必须是类型 KProperty<*> 或其超类型
这个函数必须返回与属性相同的类型(或其子类型)。
对于一个值可变(mutable)属性(也就是说,var 属性),除 getValue()函数之外,它的委托还必须 另外再提供一个名为setValue()的函数, 这个函数接受以下参数:
property —— 必须是类型 KProperty<*> 或其超类型new value —— 必须和属性同类型或者是它的超类型。
提供委托
附:
Kotlin
构造函数
Kotlin 中构造函数分为主构造函数和**次构造函数,**主构造函数只能有一个,次构造函数个数不限制,可以有一个或者多个。
- 主构造函数就是类后的小括号中定义的,会定义类有哪些数据成员并初始化。——然而那一些初始化的方法该怎么调用呢==> 在init{}中编写需要在主构造函数中完成的业务
- 而次构造函数需要用constructor关键字来声明。
基础概念:
进阶:
1 | class Person(var name: String, var age: Int) { |
- 默认情况下class是有不带参数的主构造函数, 当class具有有参的构造函数时, 无参的就会失效, 除非参数全有默认值。如果在定义了上述构造函数的情况下, 输入Person(), idea会提示报错,并提示如下Person类有如下构造函数。
但如果将主构造函数的参数全部设置为null,即class Person(var name: String? =null, var age: Int? = null)
, 那么就可以直接创建不带参数的对象了, var person0 = Person();
-
主构造函数的参数如果使用了var或val修饰符,就相当于在类中声明了对应名称的属性。
-
Kotlin 中规定,当一个类既有主构造函数又有次构造函数时,所有次构造函数都必须使用this关键字直接或间接的调用主构造函数(间接指多层调用次构造函数):
-
调用顺序: ( 伴生对象成员变量初始化 -> 伴生对象的init )-> ( 主构造函数的参数赋值 -> init 代码块 ) -> 次构造函数代码块
Throwable kotlin demo:
1 | public open class Throwable(open val message: String?, open val cause: Throwable?) { |
data class数据类
在 Kotlin 中,不需要自己动手去写一个 JavaBean,可以直接使用 DataClass,使用 DataClass 编译器会默默地帮我们生成以下函数
- equals()
- hashCode()
- toString()
- componentN()
- copy()
定义一个: dataclassCountry(var id: Int,var name: String,var continent: String)
如何申明一个简单的数据类? 有一下几点要求:
-
主构造函数必须要至少有一个参数
-
在主构造函数中的所有参数必须被标记为val或者var (var就表示可读写,val就表示只读)
-
数据类不能有以下修饰符:abstract,inner ,open, sealed ==》 数据类本身是不能被继承的 final
-
data class只能实现接口(Kotlin1.1以前的规则),现在也可以继承其它类
1 | "daf_FiledDetail") ( |
序列化
- 序列化和反序列化其实就是方便传输对象.
- 这里是需要在几个系统 Service 里通过 Bundle 传递数据.
- Parcelable 全部都在内存,效率高,
需要实现部分多,使用繁琐.(在 kotlin 不成立) - Serializable 实现简单,但是是基于反射实现的,故有性能损失,但是是最通用的.
Kotlin 中class、data class、object、companion object区别
data class:
kotlin中的数据类,只保存一些数据字段,类似于java bean,oc中的model。
1 | data class yourClassName( // 这边是小括号 |
object:
kotlin中使用"object"修饰静态类,可用于util工具类中。
- 对象声明(object declaration)
- 将类的声明和定义该类的单例对象结合在一起(即通过object就实现了单例模式)
- 对象声明中不能包含构造器(包括主构造器和次级构造器)
- 伴生对象(companion object)
- 对象表达式(object expression)
1 | object UserUtil { |
伴生对象(companion object)
因为在kotlin中是没有static关键字的,也就意味着没有静态方法和静态成员。那么在kotlin中如果想要表达这种概念,可以使用包级别函数(package-level funcation)和伴生对象(companion object)。
伴生对象语法形式
1 | class ClassName { |
- from : Kotlin:object关键字总结
相应结果封装类:
1 | package com.sucsoft.dispatch.utils |
踩坑:
- swagger里面不填时,对应传的值为空, 因此需要对应接口的参数类型上是否能接受空?, (即比在写swagger(required)上比java对写个对类型的限制),
- data class 中字段的定义, 如果需要默认值则
sortNum : Int = 0
, 而对于String的话,如果是不可为空写成id :String,
即可,如果为空则定义为id: String?
, 至于之后的默认值还是根据需求来看。
?. 和?:的区分
code1?.code2 翻译为 :if code1 Not Null 执行 code2;
code1?:code2 翻译为:if code1 Null 执行 code2;
?. 翻译为: If Not Null
?: 翻译为: If Null
注意点:
【?.】后面的方法是前者的相关调用
【?:】后面的方法与前者的无关调用
?.表示当前对象如果为空则不执行,
!!.表示当前对象如果为空也执行,然后会抛出空异常
open
在 Java 中,一个类除了被手动加上 final 关键字以外,它总能能被任意一个类继承并重写它的非 final 方法,这就可能会导致某些子类出现不符合其父类的设计初衷,特别是在多人协作的开发环境下。
这类问题被 Kotlin 语言设计者注意到了并切引起了他们的重视,因此,在 Kotlin 中的类和方法默认都是 final 的,如果要继承或者重写一个类和其方法时,必须将他们都显式地声明为 open , 成员变量不需要,会自动认为是open 的。
1 | open class Person2{ |
抛弃 Java 改用 Kotlin 的六个月后,我后悔了——Kotlin特性有哪些?
Author: Mrli
Link: https://nymrli.top/2021/09/13/学点Kotlin/
Copyright: All articles in this blog are licensed under CC BY-NC-SA 3.0 unless stating additionally.