变量与常量
语法 关键字 变量名 变量类型
val/var variantName [: Type]
val a:Int=0;
var v:View;
赋值时操作符左右类型必须匹配
函数
语法 修饰符 声明关键字 函数名 参数列表 返回类型 函数体
[open override] fun funName([params:Type[=defaultValue],…])[:ReturnType]{}
表达式函数体;
1.顶层函数
在Java中函数都是定义在类的作用域范围内,kotlin中可以在类定义以外声明函数 如
package study.kotlin
fun testKotlin(hello:String){
println(hello)
}
顶层函数的使用
在同一包范围内使用可以直接调用顶层函数,跨包使用时需要使用import关键字导入对应的函数
package com.mystudy.kotlin
fun testTopFun(str:String){
println(str)
}
//同一包作用域
package com.mystudy.kotlin
class Test1{
fun test(){
testTopFun("hello")
}
}
//跨包使用
package com.mystudy.kotlin.inner
import com.mystudy.kotlin.testTopFun
class Test{
fun test(){
testTopFun("hello")
}
}
在java代码中使用kotlin顶层函数,需要导入顶层函数所在文件
package com.mystudy.kotlin;
import com.mystudy.kotlin.TestTopKt;
public class Test2 {
public void test() {
TestTopKt.testTopFun("sf");
}
}
如果使用时希望有一个有意义的名字,可以使用注解@file:JvmName
@file:JvmName("TestUtil")
package com.mystudy.kotlin
fun testTopFun(str:String){
println(str)
}
package com.mystudy.kotlin;
public class Test2 {
public void test() {
TestUtil.testTopFun("sf");
}
}
2.扩展函数
扩展函数可以在不修改原有类的情况下,也不使用装饰者等模式,给类添加额外的功能函数,扩展函数可以直接定义在顶层,如下
跨包使用扩展函数时同样需要import
package com.test.ui
fun Activity.toast(msg: String, dur: Int = Toast.LENGTH_SHORT) {
Toast.makeText(this, msg, dur).show()
println(this.application.packageName)
}
也可以对类的伴生对象进行扩展
fun MyClass.companion.exFun(){
println("伴生对象扩展")
}
还可以在一个类(分派接受者)的内部对另一个类(扩展接受者)进行扩展,但是这种扩展只能在扩展声明的类内部使用,其他的类使用需要声明类对外提供接口;
在扩展方法中可以调用分派接受者中的方法,但是当分派接受者和扩展接受者有相同的方法定义时,优先调用扩展接受者的方法,除非显式地指明了调用者
官方api文档提到扩展方法是静态分派的,不会像java中的方法重写一样在运行时确定对象的具体类型,进而确定方法的具体实现;而是在编译期间就确定方法的实现,
根据代码中声明的对象类型确定方法实现,而不是对象的实际类型确定具体实现,所以一个声明为基类类型和子类型有一个相同的扩展方法声明,一个声明为基类行的引用指向一个子类型对象,该引用调用扩展方法调用的是基类型的实现。
http://blog.csdn.net/lihenair/article/details/73336966
3.局部函数
可以在一个函数里面定义一个函数,通常一个方法中可以进一步细化出几个小的模块,在Java中一般可以extract出几个private函数,但在结构上不太紧凑。
局部函数可以使用外部函数中定义的变量;先声明后调用
fun testLocalFun(){
val i:Int=0
fun local1(){
println("这是第一步")
println(i)
}
fun local2(){
println("这是第二步")
}
local1()
local2()
}
类与接口
语法 class className [constructor (params :Type …)][:BaseClass(params :Type …),interface …]{}
主构造函数
类名后的[constructor (params :Type …)]部分,没有修饰符或注解的情况下可省略constructor关键字
主构函数不能包含任何代码,初始化代码放在以init关键字作为前缀的初始化块中
主构函数的参数可以在初始化块中使用,也可以在类体内声明的属性初始化器(属性的初始化器不是setter,而是默认初始化表达式)中使用,不能在次级构造中使用。
没有主构造函数的情况
class TestClass2 {
fun test() {
println()
}
}
从构造函数
constructor(param:Type …):this(params …){ }
如果类有主构造函数,每个次构造函数都要,或直接或间接通过另一个次构造函数代理主构造函数
如果一个非抽象类没有声明构造函数(主构造函数或次构造函数),它会产生一个没有参数的构造函数
注意方法委托的书写方式
属性
语法
var < propertyName>[: < PropertyType>] [= < property_initializer>]
[< getter>]
[< setter>]
属性包含值得定义(java 中的字段),初始化器和getter setter三部分;
kotlin中没有字段概念backing field
默认前两种与java getter,setter有相同的功能
后两种setter不会默认设置字段值,需要使用field
var countP: Int = if (true) 0 else 1
var countS: Int = 0
get
set表达式函数体
var countQ: Int=1
// 错误写法 get() = countQ
get() =field
set(value) = println(“———–” + value)用户定义
var countR: Int=0
get() =field
set(value) {
field=value
println(“———–”)
println(“———–” + value)
}延迟初始化
lateinit关键字
内部类与嵌套类
在类的作用域内部声明一个类,有两种情况,在java中的静态内部类和内部类
kotlin中分别对应嵌套类和内部类,内部类需要使用inner关键字声明
内部类持有外部类引用,可以直接使用外部类的成员
class Nested {
fun test() {
println("-----------")
//countQ是外部类的成员,错误
//print("----- countQ------" + countQ)
}
}
inner class InnerClass {
fun test() {
println("-----------")
print("----- countQ------" + countQ)
[email protected] = 8
test()
}
}
继承重写
语法 [open abstract] class ChildClass [constructor(param:Type…)] : BaseClass [(param:Type…)]
0.kotlin和java一样只支持单继承
1.如果子类没有主构造函数,基类不能在声明时初始化,子类次级构造需要通过super调用基类的构造方法
2.在子类主构造函数中声明的参数可以在基类初始化时使用
3.默认情况下kotlin的类和方法都是final的,如果想要继承一个类,这个类必须使用open关键字声明
4.重写基类方法必须标明override关键字,且基类方法必须是open的
Object
对象表达式(Java匿名内部类)
语法
object :BaseClass[(param:Type…)][,interface]{
override fun myFun(){}
}
定义对象,不对基类做更改
对象声明(单例模式)
语法
object ObjectName:BaseClass[(param:Type…)][,interface]{
override fun myFun(){}
}
区别:指定了对象名称
伴生对象
companion object [Name] {}
在kotlin中没有static的概念,可以使用伴生对象表示Java中的类成员和类方法
伴生对象中可以定义 变量和方法,可以对伴生对象进行扩展
伴生对象可以声明名称,使用时 类名.对象名
如果不声明名字可以用 类名.Componion
空处理
kotlin为了避免空指针异常(NPE)在语法上对变量是否可以为空进行了限制,
对声明为不可空的变量,在编译期检查时,如果对变量赋予空值会报错;
对声明为可空的变量,需要自己检查变量是否为空;
变量声明
var vName : Type =null //不可空对象,编译报错
var vName : Type ? =null //可空对象
可空变量的使用
如果一个变量声明成可空类型,就不能直接使用.来access其成员
1.可以使用if判断变量是否为空,在if作用域内可直接使用.
var testNull: String? = null
if (testNull != null) {
testNull.capitalize()
}
2.安全调用符 ?. (不抛异常)
var a : String?=null
a?.capitalize()
语义: if(a !=null) a.capitalize() else null
3.非空断言 !! (抛出NPE异常)
确定不为空可以使用(没必要了,有点鸡肋);
对空变量使用非空断言将会抛出NPE异常
4.作为参数
fun test(param:String){
println(param)
}
var a : String?=null
a?.let{
a->test(a)
}