在使用Kotlin过程中经常会有一些新的概念出来,这里在在学习过程中比这些新概念做了一个记录。
- 函数
- 标签
- 伸展(spread)
- 中缀
- 局部函数
- 成员函数
- 区间
- 对象表达
- 安全类型转换
函数
kotlin 函数以fun来标识,使用方式和java一样,同一类使用直接调用,如果是通过类引用则使用 ‘类名.model()’ 来引用
1 | fun mode(){ |
当前类使用
1 | var value = model() |
其他类
1 | 类名.mode() |
根据返回的值自动推断类型 return string Type
1 | fun model() = "jinwei" + 20 |
根据返回的值自动推断类型 return int Type
1 | fun model2() = 32 |
等价于 fun model3()
1 | fun model3() = Unit |
标签
标签是为了方便跳转到指定位置,常和 break 和 continue 搭配使用。但是Kotlin中的标签还可以和 return 搭配使用,表示在标签处返回。
simple
这段代码的j==2时调用了break,所以这次的执行被返回了出去,继续下一次i的执行;1
2
3
4
5
6
7
8
9fun f() {
for (i in 0..10) {
for (j in 0..2) {
if (j == 2) {
break
}
}
}
}
如果我们希望break调整个循环体,就可以用标签了。
1 | lable@ |
这种方式命名,name自己定义后面跟入一个@符号
这次加入了标签,break到了loop标签的地方。直接就会结束标签定义的地方,继续往下执行。
1 | fun f() { |
标签处返回
1 | fun main(args: Array<String>) { |
上面代码中的 return 指的是从 main 函数中返回,因为 main 函数是最直接包围它的函数。所以运行结果为:
123
如果想要从 forEach 中的 lambda 表达式中返回,就需要使用标签了。代码如下,在 lambda 表达式的前面声明一个标签lit@
,然后在 return 处使用标签,即[return@lit](mailto:return@lit)
。
1 | val ints = intArrayOf(1, 2, 3, 0, 4, 5, 6) |
运行结果为:
123456
可变操作参数
参数的最后一个或者第一个可以被vararg标记,变量会变成array类型,类似于java中的 int…可变参数一样。
1 | fun mode2(vararg ts: Int) { |
伸展(spread)操作符,可以将数组传递给函数。
可以看到在变量的前面加入了*符号1
2var v = intArrayOf(1, 2)
mode2(1, 1, *v)
中缀
中缀表示法三个条件
- 它们必须是成员函数或扩展函数
- 它们必须只有一个参数;
- 其参数不得接受可变数量的参数且不能有默认值
- Int.model代表只能通过Int来调用或者是其他的类型,基本类型,对象都可以。
- this代表调用的的对象
- 这里只能通过Int来调用,其他类型会编译错误 1 model2 2
1 | infix fun Int.model2(v: Int): Int { |
中缀函数调用的优先级低于算术操作符、类型转换以及 rangeTo 操作符
1 | 1 model2 2 * 2 |
等于 1 model2(2*2) 先会运行有算数运算符的逻辑,才去执行方法的调用
另一方面,中缀函数调用的优先级高于布尔操作符 && 与 ||、is- 与 in- 检测以及其他一些操作符
1 | 1 model2 2 == 2 |
先执行前面的中缀,在执行布尔运行
局部函数
局部函数,内部函数可以访问外部变量。
1 | fun model3() { |
成员函数
在类或者对象的内部声明,通过实例来调用。
1 | class Simple { |
泛型函数
1 | fun <T> getTbean(item: T): T { |
数组
kotlin 使用Array来表示数组,通过库函数arrayOf来创建。
1 | fun test() { |
字符串
1 | fun stringTest() { |
区间
0..10执行0-10直接的数1
2
3for (item in 0..10) {
}
倒序
执行4-1之间的数1
for (i in 4 downTo 1) print(i)
step
step 3代表每隔3打印一次 打印124
1 | for (i in 1..10 step 3) println(i) |
要创建一个不包括其结束元素的区间,可以使用 until 函数
1 | for (i in 1 until 10) { |
对象表达式
类似于java中的匿名内部类
1 | //这是一个对象 |
伴生对象
这是一个伴生对象,可以直接通过类名来使用A内部的方法和变量
1
2
3companion object A {
fun create() {}
}
数据类
数据类通过data 来标识类,特有以下特点。
编译器自动从主构造函数中声明的所有属性导出以下成员:
equals()
/hashCode()
对;toString()
格式是"User(name=John, age=42)"
;componentN()
函数 按声明顺序对应于所有属性;copy()
函数(见下文)。
1 | data class JinBean(var name: String, var age: Int) {} |
普通对象是不会具有这类特性的。
1 | var data = JinBean("JINWEI", 20) |
输出 20
输出 22
通过copy没有影响到对方的值,如果没有data的话。这里的输出会是一样的,因为他们指向的引用到是同一个对象。
内部类
1 | class TT2 { |
解构
定义一个实体类1
2
3
4
5
6
7
8data class P2(var age: Int, var name: String) {
var number = 2
operator fun component3(): Int {
return number
}
}
- 解构声明,意思就是通过var(x,x) = p的方式把对象的构造参数,通过拆解的方式单独声明一个变量。
1 | var pp: P2 = P2(20, "jw") |
在Kotlin-数据类中,我们已经了解到编译器会根据主构造器中声明的全部属性, 自动推断产生componentN() 函数群, 这些函数与类的属性对应, 函数名中的数字1到N,与属性的声明顺序一致
解构声明在编译时将被分解为以下代码:
1 | val name = person.component1() |
- 解构for循环
1 | var p1 = P2(20, "jw") |
自定义componentN,默认解构只会解构造函数里面的变量,如果需要解构我们自己的成员变量,就需要自定义了。
对于自定义的componentN(),需要注意以下几点:
- componentN()函数需要标记为 operator , 才可以在解构声明中使用
- componentN()函数的返回值类型必须与属性类型一致
1 | var p4 = P2(20, "jw") |
- Map
1 | var p11 = P2(210, "jw") |
安全类型转换
如果对象不是目标类型,那么常规类型转换可能会导致 ClassCastException。 另一个选择是使用安全的类型转换,如果尝试转换不成功则返回 null:
1 | var v1: String? = "" |
使用安全转换
1 | var v1: String? = "" |
as?这样转换不会报错,会返回null。