val用来声明常量,var用来声明变量。
声明一个变量格式如下:
var count: Int = 10
声明一个常量格式如下:
val name: String = "kotlin"
局部范围的常量允许在声明时不指定初始值,只要在第一次使用之前指定初始值即可。
赋予初始值后,kotlin编译器可可根据所赋值的类型来推断其类型。
比如上边的name可以写成如下形式:
val name = "kotlin"
在变量类型后加?后缀,可以把变量指定为null,带?的类型支持被赋予null值。
val name: String? = "kotlin"
kotlin规定不同整型的变量或值之间必须进行显示转换。
Any类是所有类型的根父类,javaClass属性用于获取指定变量对应的Java类型,大致相当于Java的getClass()方法。
使用?.进行安全调用,比如a?.length,如果a不为null则返回a的长度,否则返回null。
?:运算符,如果左边的表达式不为null,则返回左侧表达式的值,否则返回右侧表达式的值,比如a?.length ?: -1。
!!.强制调用运算符可强制调用可空变量的方法或属性,比如a!!.length。
String类型允许通过s[i]访问特定位置的字符。
通过将变量或表达式放入字符模板中允许在字符串中嵌入变量或表达式,比如“字符长度:${a.length}”。
使用typealias 可以为已有类型定义别名,语法:typealias 类型别名 = 已有类型
运算符
kotlin的运算符都是以方法形式来实现的,各种运算符对应的方法名都是固定的,只要提供了特定名称的方法,就能执行特定的运算。
b.contains(a) <=> a in b
kotlin的”==“基本等于Java的equals方法,”===“和”!==“代替Java的”==“和”!=“。
区间运算符包括闭区间运算符和半开区间运算符。闭区间运算符a .. b用于定义一个a~b(包括a, b边界值)的所有值的区间。半开区间运算符a until b用于定义一个a~b(包括a不包含b)的所有值的区间。
downTo 运算符定义一个从大到小的反向区间。
默认区间步长都是1,通过step,可以指定区间步长,比如:a downTo b step 2。
重载运算符的方法需要用operator修饰符进行标记。比如:operator fun inc()。
流程控制
kotlin提供了条件表达式和when表达式来简化条件语句。
// 条件表达式
var i: Int = 10;
val name: String = if (i > 10) {
"1"
} else if (i < 10) {
"2"
} else {
"3"
}
println(name)
// when表达式
val name2: String = when {
i > 10 -> "1"
i < 10 -> "2"
else -> "3"
}
数组和集合
Array<T>类代表数组。
创建数组的两种方式:
1、使用arrayOf()、arrayOfNulls()、emptyArray()工具函数
2、使用Array(size: Int, init: (Int) -> T)构造器
当然还有IntArray, DoubleArray等
intArrayOf()等函数
比如 val prices = arrayOfNulls<Double>(5)
prices.size可以访问数组的长度, 数组的withIndex()方法可以返回一个Interable对象,它允许同时访问数组的索引和元素
asList() 将数组转换成List集合。
集合分为可变集合和不可变集合,不可变集合只能读取元素。
kolin的集合实现类只有HashSet,LinkedHashSet和ArrayList三个。
遍历map的方式
val strMap = mapOf<Int, String>()
for ((key, value ) in strMap) {
}
strMap.forEach()
函数和Lambda表达式
请使用fun关键字声明函数,后跟函数名,接下来定义函数接受的参数,然后声明返回值类型。
如果想声明一个函数没有返回值,有两种声明方式:
1、省略”:返回值“部分;
2、使用”:Unit“作为返回值,Unit相当于Java的void;
如果函数体只有一个表达式,那可以用=指定函数体即可,并可以省略返回值。
函数的形参可以指定默认值,调用函数时可是省略该形参,比如fun exec(name: String = "Dog"),那么调用时可以直接 exec(),省略参数调用,Kotlin建议将带默认值的参数放在函数参数列表的后面。
个数可变的参数可以接受多个参数值,类似Java的int...,需要用vararg修饰,比如vararg name: String,其本质就是一个数组,如果要传入数组,需要在数组前添加*,比如 *names。
在函数体内部定义的函数称为局部函数。
fun getName(value: Int): String {
val result = if (value > 10) {
"java"
} else if (value < 10) {
"C++"
} else {
"kotlin"
}
return result
}
函数可以简化为
fun getName(value: Int): String {
return if (value > 10) {
"java"
} else if (value < 10) {
"C++"
} else {
"kotlin"
}
}
还可以将return关键字替换为赋值运算符
fun getName(value: Int): String = if (value > 10) {
"java"
} else if (value < 10) {
"C++"
} else {
"kotlin"
}
我们可以定义一个匿名函数并持有一个对此函数的引用
val stringLengthFunc: (String) -> Int = { input ->
input.length
}
val stringLength: Int = stringLengthFunc("Android")
将其他函数用作参数的函数高阶函数。
函数类型由形参列表,->和返回值类型组成,比如(int) -> Int
函数类型可以作为形参使用,如下:
fun stringMapper(str: String, mapper: (String) -> Int): Int {
// Invoke function
return mapper(str)
}
访问一个函数的函数引用时,在函数名前添加两个冒号。
函数类型也可作为返回值类型。
Lambda表达式
{(形参列表) ->
// 执行语句
}
Lambda表达式总是被大括号括着,执行语句的最后一句自动被作为Lambda表达式的返回值,无须使用return关键字。Lambda表达式和函数类似,传入参数,返回结果。
val square :(Int) -> Int = {n -> n * n}
如果只有一个形参,kotlin允许省略形参和->,并用it代表形参。
val square :(Int) -> Int = {it * it}
如果函数的最后一个参数是函数类型,并且打算传入Lambda表达式,那么允许在圆括号之外指定Lambda表达式。
cal(1){it * it}
fun cal(value :Int, me: (Int) -> Int) :Int {
return me(value)
}
Lambda表达式不能指定返回值类型,此时可以使用匿名函数代替它。
使用inline关键字修饰带函数形参的函数就是内联函数,使用内联函数可以省略函数调用产生的时间和空间开销。
类和对象
[修饰符] class 类名 [constructor 主构造器] {}
Kotlin创建对象不需要使用new关键字,类可以有01个主构造器和0多个次构造器,主构造器是类头的一部分,它跟在类名后。
class User constructor(name: String) {}
如果主构造器没有任何注解或修饰符,则可以省略constructor关键字,如下:
class User(name: String) {}
如何类没有任何构造器,则系统自动提供一个无参数的主构造器,该构造器默认用public修饰,一但提供了构造器,则系统不再提供默认的。
定义属性的语法格式:
[修饰符] var/val 属性名: 类型 [= 默认值]
[<getter>]
[<setter>]
定义构造器的语法格式:
[修饰符] constructor (形参列表) {
// 执行语句
}
方法使用infix修饰,就可通过中缀表示法调用,infix方法只能有一个参数。
如果希望将对象解构给多个变量,则必须为该对象定义componentN()方法,且该方法需要使用operator修饰。
operator fun component1(): String{
return name;
}
var (name, age) = user
如果希望忽略某个属性,用"_"进行占位即可。
数据类用于封装数据,数据类使用data修饰,主构造器至少需要有一个参数,所有参数需要用val或var声明为属性。
data class Result(val result: Int, val status: String)
系统自动为数据类生成equals()/hashCode()方法,为每个属性自动生产operator修饰的componentN()方法,生产toString(),copy()方法。
Kotlin提供了Pair和Triple两个数据类。
kotlin会为只读属性生成getter方法,为读写属性生成getter和setter方法,Kotlin不允许直接调用生成的getter,setter方法。
在定义属性时可指定自定义的getter和setter方法,无须使用fun关键字,getter方法形如get() {},setter方法形如set(value) {}。
var nameStr: String = "lkk"
get() {
println(nameStr)
return "${nameStr} + ${name}"
}
Kotlin会自动为普通属性生成一个field字段,称为幕后字段,在getter,setter方法中通过field关键字引用幕后字段。
set(newName) {
if (newName.length > 10 || newName.length < 0) {
println("illegal")
} else {
field = newName
}
}
用private修饰的属性为幕后属性,幕后属性不会自动生成getter,setter方法,需要开发者提供。
Kotlin要求所有属性必须显示初始化,要么在定义属性时赋初始值,要么在构造器中对该属性赋初始值。
使用lateinit修饰的属性可以延迟初始化。
kotlin提供了private,protected,internal和public四个访问控制符。
private:与Java的private类似,只能在该类的内部或文件内部被访问。
internal:可以在该类内部或文件内部或同一个模块内被访问。
protected:可以在类内部或文件内部或其子类中被访问。
public:可以在任意地方被访问。
如果没指定修饰符,那默认的访问控制修饰符是public。
Kotlin取消了Java的默认访问权限,引入了internal访问控制符,取消了protected的包访问权限。
包内的顶层成员(包括顶层类,接口,函数,属性)只能使用private,internal,public,不能使用protected修饰符,顶层成员会转换成Kotlin所生产的类中的静态方法和静态属性。
属性的getter方法的访问权限总是与该属性保持一致。
Kotlin的主构造器类似Java的初始化块,并能通过形参传入参数,主构造器的主要作用就是为初始化块定义参数。
初始化块的语法格式:
init {
// 执行代码,可使用主构造器定义的参数
}
所以的次构造器必须委托调用主构造器(执行初始化块中的代码),然后才执行次构造器代码。
如果两个构造器中有相同的初始化代码,可以把它们放在初始化块中定义,如果需要参数,则可将参数放在主构造器中定义。
class MainViewModel(private val name: String): ViewModel() {
init {
val s = name
}
constructor(): this("hello") {
println(name)
}
constructor(age: Int): this() {
}
}
Kotlin使用 :this(参数) 语法委托另一个构造器。
继承的语法格式
修饰符 class SubClass: SuperClass {
// 类定义
}
Any类是所有类的父类,Any类只有equals(),hashCode()和toString() 3个方法。
Kotlin的类默认有final修饰,因此Kotlin的类默认是不能派生子类的,为了让一个类能派生子类,需要使用open修饰该类。
kotlin默认为所有方法添加final修饰符,阻止该方法被重新,可添加open关键字。
类重写父类的方法必须添加override修饰符。
父类被重写的属性必须使用open修饰,子类重写的属性必须使用override修饰。
可以在子类中使用super<T>的形式引用超类型中的成员。
使用is运算符进行类型检查,as运算符进行强制转型,as?安全的强制转型运算符,如果转型失败,不会引发异常,而是返回null。
kotlin默认为所有类,方法,属性都提供了final关键字修饰,则kotlin的类不可派生子类,方法,属性不可被重写。
抽象类定义多个类的模板,接口则定义多个类的规范。
扩展方法其实就是定义一个函数,在函数名前增加被扩展的类名和点号,扩展的方法就是给类增加方法一样。
-扩展可动态地为已有的类添加方法或属性;
-扩展能以更好的形式组织一些工具方法;
kotlin的final不能修饰局部变量。
const 用来修饰可执行宏替换的常量,也称编译时常量。
抽象类
有abstract修饰的成员,无须使用open修饰。
密封类是一种特殊的抽象类,专门用于派生子类,密封类的子类必须与密封类本身在同一个文件中,密封类用sealed修饰,密封类的所有构造器都必须是private的。
接口
[修饰符] interface 接口名: 父接口 {}
接口即可包含抽象方法,也可包含非抽象方法。
只有将一个类放在另一个类中定义,这个类就成了嵌套类,相当于Java的静态内部类,嵌套类不可访问外部类的其他任何成员。
使用inner修饰的嵌套类叫内部类,相当于Java的非静态内部类,内部类可直接访问外部类的所有成员。
对象表达式其实是增强版的匿名内部类,对象表达式的语法格式如下:
object [: 0 ~N个父类型] {
// 对象表达式的类体部分
}
对象声明的语法格式如下:
object ObjectName [: 0~N 个父类型] {
// 类体部分
}
对象声明专门用于实现单例模式,对象声明所定义的对象也就是该类的唯一实例,程序可通过其名称直接访问该类的唯一实例。
在类中定义的对象声明,可使用companion修饰,这样对象就变成了伴生对象,每个类最多只能定义一个伴生对象,伴生对象相当于外部类的对象,可通过外部类直接调用伴生对象的成员,伴生对象为其所在的类模拟静态成员,伴生对象可省略名称,外部类可通过companion访问伴生对象。
类委托和属性委托
类委托是将本类需要实现的部分委托给其他对象,相当于借用其他对象的方法作为自己的实现。通过by关键字指定委托对象。为类指定委托对象的两种方式,第一种是通过构造器参数指定委托对象;第二种是直接在类定义的by后新建对象。
委托属性可以将多个类的类似属性统一交给委托对象集中实现。
延迟属性是一个lazy()函数,接受一个lambda表达式作为参数,并返回一个Lazy<T>对象。
属性监听通过 by Delegates的observable或vetoable方法,vetoable需要返回值,当返回true时属性的新值设置成功,否则设置失败。
参考:
https://developer.android.com/kotlin/learn?hl=zh-cn#higher-order
《疯狂Kotlin讲义》
网友评论