前言: 用了
kotlin
差不多半年时间了,之前是看了中文版的kotlin
入门的,后面使用的时候总感觉有些细节的东西并没有很好的理解,专门买了份kotlin
的教程,感觉不错,本文也是中途一些细节的整理。
1. 成员变量&空类型
var
用lateinit
延迟初始化,val
用lazy
延迟初始化- 类型后面跟
?
表示可空类型,类型后面不跟?
表示不可空类型- 可空类型使用 :
?.
表示若为null
,则返回null
,否则返回返回对象继续逻辑操作。!!.
表示强制认定为不为null
使用但是最好自己做一下判断,不然等下抛空异常?:
表示当前面为null
的时候,返回?:
后面的值
class X
lateinit var mStr:String //var 延迟初始化
val x:X by lazy{ // val延迟初始化
X()
}
var name: String? = ""
fun a(x: Any): Int {
return name?.length ?: 0
}
val nullable:String? = null //正确 可以为空
val notNull:String = null //报错 不能为空
nullable.length //错误 ,不可以直接使用
notNull.length //正确,不可空的值可以直接使用
nullable!!.length //正确,强认定不为null
nullable?.length //正确,若nullable为空
nullable?.length ?: -1
2. 类型转换
as
类型转换 跟java的类型转换一样,失败则抛异常
as?
安全类型转换,转换失败返回null,不会抛异常
val stub:Childen = parent as Childen //转换失败 stub不会返回空,会抛异常
val stub:Childen = parent as? Childen //转换失败 stub 可以返回空
3. 区间
ClosedRange
的子类,IntRange
最用
- 半闭区间 ..
- 开区间 until
- 基本写法:
0..100
表示[0,100]
0 until 100
表示[0,100)
i in 0..100
判断是否在区间[0,100]
中
val range:IntRange = 0..1024 //[0,1024]
val range_exclusive:IntRange = 0 until 1024 //[0,1024) = [0,1023]
val emptyRange:IntRange = 0...-1 //空区间
emptyRange.isEmpty() //判断区间是否为空
//迭代
for(i in range)
4. 数组
基本写法: val array:Array<类型> = arrayOf(类型的对象1,类型的对象2,.....)
基本操作:
array[i]
:输入第i
个成员
array[i] = 新的类型对象
给第i
个成员赋值
array.slice()
截取 可传入区间
eg:0..10 或者 0 until 100
array.joinToString()
转化成String
下面是对应的参数
--separator: CharSequence
:分隔符 默认为,
--prefix: CharSequence
: 增加前缀 默认为""
--postfix: CharSequence
:增加后缀 默认为""
--limit: Int
:截取位置(从1开始而不是从0开始)
--truncated
:被limit截取后,后面展示什么
--transform
: 每个元素进行处理返回处理后的数据
具体例子展示:
val arrayOfInt: IntArray = intArrayOf(1,3,5,7)
val arrayOfChar: CharArray = charArrayOf('H', 'e','l','l','o','W','o','r','l','d')
val arrayOfString: Array = arrayOf("我", "是", "码农")
fun main() {
System.out.println(arrayOfString.joinToString(""))
var i:IntRange = 0..100
System.out.println(arrayOfChar.slice(0..7))
var array1 = arrayOfString.joinToString("","前缀","后缀",2,"后面省略",transform = {
//转换
return@joinToString it+"转换"
})
var array2 = arrayOfString.joinToString("","前缀","后缀",3,"后面省略",transform = {
//转换
return@joinToString it+"转换"
})
println(array1)
println(array2)
}
-------------------------------------打印出来的log
我是码农
[H, e, l, l, o, W, o, r]
前缀我转换是转换后面省略后缀
前缀我转换是转换码农转换后缀
5. 变量
val TAG = "str"
并不等价与 java中的
public final String TAG = "str"
如果要等价的话
const val TAG = "str"
具体的可以看一下反编译
val TAG = "str"
的字节码跟public final String TAG = "str"
有什么不同~
里面可以看到val
虽然不可赋值,但是它并不是编译器常量,而是一个变量。也就是编译期并不是替换成常量来进行的,所以后期还是可以经过反射进行修改! 如果要像java中定义出编译期常量的话public final String TAG = "str"
则需要使用到const
这里反编译出来的跟java
中的就类似了。
6. 函数与lamba表达式
- 匿名函数写法
本质函数也是一个成员,只不过它是一段代码块
val int2Long = fun(x:Int):Long{
return x.toLong()
}
- Lambda表达式
本质:匿名函数
写法:{[参数列表]->[函数体,最后一行还是返回值]}
举例:val sum = {a:Int,b:Int ->a+b}
调用:
-- 用()进行调用
-- 等价于 invoke()
--eg: sum(1,2) 或者 sum.invoke(1,2)
简化:
-- 函数参数参数调用是最后一位Lambda
可以移出去
-- 函数参数只有一个Lambda
,调用时小括号可省略
--Lambda
只有一个参数可默认为it
-- 入参、返回值与形参一直的函数可以用函数引用的方式作为实参传入
示例一下:
//首先 先看下Arrays里面的forEatch方法
public inline fun CharArray.forEach(action: (Char) -> Unit): Unit {
for (element in this) action(element)
}
-------------------------------------------------
接下来,代码中调用
---------------------------------------------------
val arrayOfChar: CharArray = charArrayOf('H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd')
//最基本的调用,传入action的代码块
arrayOfChar.forEach(char ->{println(char)})
// 简化
//1. Lambda只有一个参数可默认为it
arrayOfChar.forEach ({println(it)})
// 2. 函数参数参数调用是最后一位Lambda可以移出去
arrayOfChar.forEach(){pritln(it)}
// 3. 函数参数只有一个Lambda,调用时小括号可省略
arrayOfChar.forEach{pritln(it)}
// 4. 入参、返回值与形参一致的函数可以用函数引用的方式作为实参传入
arrayOfChar.forEach(::println)
7. 操作符
与
java
不同,kotlin
的操作符允许我们进行重载。重载的函数需要使用operation
的修饰符标记
例子:
// 重写 减号的实例
class TestKotlin(var a:Int) {
operator fun minus(i:Int):Int{
return a - i
}
}
fun main() {
println(TestKotlin(6) - 5)
}
---------------------输入
1
很多的操作符那些可以看一下 《Kotlin语言中文站》
8. 表达式(中缀 分支 when 等)
- 中缀函数
只有一个参数,且用infix
修饰的函数,使用时不需要对象.方法名(参数)
,直接对象 方法名 参数
//定义
class Book { infix fun on(palce:String){...}}
//使用
Book() on "My Name"
- 分支函数
-
for
循环的in
把鼠标点击in
可见其循环机制实际上是iterator
机制,hasNext()
跟next()
,判断有没有下一个值,有就获取
下面我们自己自定义一个MyIterator
,跟MyIntList
来演示一下
class MyIterator(val iterator:Iterator){
operator fun next():Int{
return iterator.next()
}
operator fun hasNext():Boolean{
return iterator.hasNext()
}
}
class MyIntList{
private val list = ArrayList()
fun add(int:Int){
list.add(int)
}
fun remove(int:Int){
list.remove(int)
}
operator fun iterator():MyIterator{
return MyIterator(list.iterator())
}
}
fun main() {
val list = MyIntList()
list.add(1)
list.add(2)
list.add(3)
for (i in list){
println(i)
}
}
- 跳过跟终止循环(
continue
跟break
)
跟java
一致,跳过当前循环用continue
,终止循环用break
- 多层循环嵌套的终止结合标签使用
Outter@for(...){
Inner@while(i<0){if(...)break@Outter}
}
可以定义标签,对应关键字后面加上@标签,代表跳出到对应的某一层,
举个例子:
fun main() {
val list = MyIntList()
list.add(1)
list.add(2)
list.add(3)
list.add(4)
list.add(5)
//break到上一层
for (i in list) {
print(" $i")
while (true) {
print(" $i")
break
}
}
println(" ")
//break到制定标签
OutSide@for (i in list) {
print(" $i")
while (true) {
print(" $i")
break@OutSide
}
}
}
---------------------打印---------------
1 1 2 2 3 3 4 4 5 5
1 1
if else
分支表达式等与java
的不同点,它们作为表达式,可以用来赋值,最后一个行的数据等于return
的数据
fun compare():Int{
val i = 10
if (i == 10 ) 1 else 0
}
9. 异常捕获
与
java
不同的是,它也是表达式,最后一行的数据也是当成要返回的值。
但是 注意下面的写法:
return try{x/y}catch(e:Exception){0}finally{...}
先执行完try
有异常就跑到catch
,最后finally
的代码会执行,最后再返回值。(如果正常返回0
,抛异常就返回x/y
)
10. 参数 (具名,变长,默认)
所有参数都有一点:如果传参时有歧义,需要使用具名参数
- 默认参数
fun sum(arg1:Int = 3,arg2:Int = 2) = arg1+arg2
- 具名参数(vararg 变量名:类型)
给函数的实参附上形参
fun sum(arg1:Int,arg2:Int) = arg1+arg2
sum(arg1=2,arg2=3)
- 变长参数
某个参数可以接受多个值
与java
不同的是,它可以不用做最后一个参数,kotlin
支持具名函数,所以它可以放在参数中任意位置。
fun hello (vararg ints:Int,string:String){ ints.forEach(::println)}
使用:
hello(1,2,3,4,string="Hello")
- Spread Operator(
*变量名
)
应用场景:
只支持展开Array,只用于变长参数列表的实参,不能重载,不是一定意义上的运算符
fun hello (double:Double,vararg ints:Int,string:String){ ints.forEach(::println)}
使用:
val array = intArrayOf(1,2,3,4)
hello(3.0,*array,string="Hello")