Android - kotlin基础

基础可以先看这里,深入可以看下官网

以下只记录要点

常量与变量

常量是 val
变量是 var
全局变量需要在声明时被初始化,如果无法初始化可加上lateinit来声明这个变量可延迟初始化

lateinit var a:String

fun main(args: Array<String>) {
    var b = "bbbb"
    val c = "cccc"
    a = "aaaaaa"
    println(a)
}

虽然val是不可变的,但是也不完全等同java的final,比如比如

class Test{
	val name = "dean"
		get(){
			//虽然name是val,但是在取值时仍然能够被修改
			return field + " hello"
		}
}

空安全设计

var name:String = null

这样会报错,因为name这样声明是不能为空的,如果非要这样需要声明时指定该变量可能为空

加上?解除非空限制
var name:String? = null

但是在使用该变量时需要注意

name.length

直接这样写会报错,因为name可能为空,如果自行用if来判断是否为null,会有现车不安全问题,应该加上?.或者!!.来处理。

?.代表name不为空时调用length方法, ?: 可加可不加,加上代表name为空时length的值
var length = name?.length ?: 0!!.时就会绕开kotlin的空安全设计,和java差不多了,如果name为空,就会抛空指针异常
var length = name!!.length

还有注意如果是可空变量,传值时不能传给不可空参数

var name: String? = "dean"
fun filterName(name: String){
}

filterName(name) 会报错

最后如果是基本类型,尽量不声明为可空,因为声明为可空时会装箱,增加性能开销

var length: Int = 1		不装箱
var length: Int? = 1	装箱

var list = listof(1, 2) 装箱
var array = intArrayof(1, 2) 不装箱

类型检测会自动转换类型

在java中判断类型用instanceof,在判断往后需要手动转换为需要的类型,kotlin不需要

fun getStringLength(any: Any): Int?{
	if (any is String){
		//如果any是String类型,此时就已经被转换成String了可以当成String使用
		return any.length
	}
	//如果any不是String类型,没有数据转换,即any还是Any类型。
	return null
}

一切皆对象

在kotlin中一切皆对象,因为kotlin是基于jvm的,所以和java一样,int值在-128到127之间是用享元模式写的,也就是他们拿到的对象是同一个。

fun main(args: Array<String>) {
    val a: Int = 127
    val boxedA: Int? = a
    val anotherBoxedA: Int? = a
    println(boxedA == anotherBoxedA)		//输出true
    println(boxedA === anotherBoxedA)		//输出true,因为都是从缓存池里面拿的
}

fun main(args: Array<String>) {
    val a: Int = 128
    val boxedA: Int? = a
    val anotherBoxedA: Int? = a
    println(boxedA == anotherBoxedA)		//输出true
    println(boxedA === anotherBoxedA)		//输出false,因为缓存池里面没有,新创建出来的
}

默认类型

整数默认类型是Int,浮点数默认类型是Double

val a = 1000
val b = a / 2 //a和b都是Int

val c = 2.5   //c是Double

数值比较

NaN与其自身相等

NaN比包括正无穷大在内的任何其他元素都大

-0.0小于0.0

if和when可以当做表达式

java中的switch在kotlin中变成了when,并且功能更加强大,条件多变,不仅可以是多种类型,还可以是一些方法的返回值,in / is 等。

除此以外if和when还可以被当做表达式

kotlin不需要java中的三木运算符,因为if可以替代,这边判断代码块里面的最后一行就是b的值
val b = if(a > c){
	。。。。
	 1 
}else 2

when 作为表达式必须要有else,除非编译时能够判断出x的所有情况被包含,比如enum
val d = when(x){
	is String -> {
		println("x is String")
		"string  啊"
	}
	is Int -> {
		println("x is Int")
		"Int  啊"
	}
	else -> {
		"不知道类型  啊"
	}
}

when 后面的判断参数可以省略,那个分支为true就用哪个

fun main(array: Array<String>){
    val a = "abc"
    when{
        a.startsWith("c") -> println("a start with c")
        a.startsWith("b") -> println("a start with b")
        a.startsWith("a") -> println("a start with a")
    }
}

return返回

在内部函数的lambda表达式中直接使用return会直接跳出外层函数

fun foo(){
	listOf(1,2,3,4,5).forEach{
		//这边直接返回foo的调用者,而不是跳出forEach函数的调用
		if(it == 3) return
	}
	println("foo")
}

如果只是想跳出内部函数可以不用lambda或者使用标签

fun foo(){
	listOf(1,2,3,4,5).forEach{
		//表示当前循环调过3这种情况
		if(it == 3) return@forEach
	}
	println("foo")
}

如果需要跳出循环,需要显示跳转到外部标签

fun foo(){
	run myLoop@{
		listOf(1,2,3,4,5).forEach{
			if(it == 3) return@myLoop
		}
	}
	println("foo")
}

静态方法、静态常量、单例

如果希望整个项目能够使用的常量或者方法,可以直接现在kt文件顶部,即top level

如果是不同包下同名称的顶级函数也是没关系,调用的时候会根据包名路径来区分
fun dp2dip(){
	...
}

fun dp2px(){
	...
}

这样在项目的其他地方可以直接使用,就好像这个方法写在本类内部一样
dp2dip()
dp2px()

如果一个类是单例直接在声明时将class改为object

open class A{
    open fun aMethod(){
        println("a method")
    }
}

interface B{
    fun bMethod()
}

object C : A(), B {
    override fun aMethod() {
        super.aMethod()
        println("c extends a method")
    }
    override fun bMethod() {
        println("c extends B method")
    }
}

调用
C.aMethod()
C.bMethod()

如果只需类有一些静态方法或静态常量则写在companion object里面

class A{
	//嵌套对象,外部通过A.D.dMethod来调用
    object D{
        fun dMethod(){
            println("d method")
        }
    }
}

可以加上伴生对象关键字companion来修饰
open class A{
	//这时名称D一般省略,因为companion标识D是A的伴生对象,一个类的伴生对象只能有一个(嵌套对象可以多个),因此写类名也就没必要,外部调用可以直接用 A.LOG_TAG
	companion object D {
		const val LOG_TAG = "A"
		fun staticMethod(){
			println("D static method")
		}
	}
}

object还可以用来创建匿名类

val listener = object: View.OnClickListener(){
	override fun onClick(view: View){
	}
}

常量const

const只能出现在顶层或者companion object中,并且只能修饰基本数据类型和String类型。

const val LOG_TAG = "aa"
class A{
	companion object{
		const val LOG_TAG = "aa"
	}
}

可变集合和不可变集合

listOf、setOf、mapOf创建的是不可变集合,mutableListOf、mutableSetOf、mutableMapOf创建的是可变集合。但是不可变集合可以通过toMtable来转换成相应的可变集合

不可变指集合的长度和内容

fun main(array: Array<String>){
    val list = listOf("a", "b")
    list.forEach {
        println(it)
    }
    list.toMutableList()

    val list2 = list.toMutableList()
    list2.add("c")
    list2.forEach {
        println(it)
    }
}

数组和集合的操作符

  • forEach:遍历每个元素
var intArray = intArrayOf(1, 2, 3)
intArray.forEach { println(it) }

输出
1
2
3
  • filter:对每个元素进行过滤操作,如果满足条件保留其余去除,最终生成新的集合
var intArray = intArrayOf(1, 2, 3)
intArray.filter { it > 2 }.forEach { println(it) }

输出
3
  • map:遍历每个元素并执行给定表达式,形成新的集合
var intArray = intArrayOf(1, 2, 3)
intArray.map { it + 1 }.forEach { println(it) }

输出
2
3
4
  • flatMap:遍历每个元素,并为每个元素创建新的集合,最后合并到一个集合中
var intArray = intArrayOf(1, 2)
intArray.flatMap { i -> listOf(i, "i is $i") }.forEach { println(it) }

输出
1
i is 1
2
i is 2

惰性集合Sequence

Sequence当找到需要的结果时,就不会往下处理,没有使用时,也不会处理

fun main(array: Array<String>){
    val sequence = sequenceOf(1, 2, 3, 4, 5)
    val result = sequence.map {
        println("sequence map $it")
        it + 1
    }.filter {
        println("sequence filter $it")
        it > 2
    }
	//当result.first()被调用时,上面代码才会被执行,并且一旦找到了result的第一个元素,上面代码也不会往下执行了
    println(result.first())
}

结果
sequence map 1
sequence filter 2
sequence map 2
sequence filter 3
3

对比普通的集合类,每调用一次函数不会生成新的Iterable,Sequence在数据量较大或者数据量未知时,可以减少性能消耗

集合性能对比

同样是1到100_000,平均值计算,intArray快很多

fun main(array: Array<String>){
    //array 6ms
    println("array start time ${System.currentTimeMillis()}")
    val array= Array(100_000){ i -> i * 1 }
    var arraySum = 0
    array.forEach { arraySum += it }
    println(arraySum / array.size)
    println("array end time ${System.currentTimeMillis()}")

    //intArray 3-4ms
    println("int array start time ${System.currentTimeMillis()}")
    val intArray = IntArray(100_000){i -> i * 1}
    var intArraySum = 0
    intArray.forEach { intArraySum += it }
    println(intArraySum / intArray.size)
    println("int array end time ${System.currentTimeMillis()}")

    //list 9ms
    println("list start time ${System.currentTimeMillis()}")
    val list = List(100_000){i -> i * 1}
    var listSum = 0
    list.forEach { listSum += it }
    println(listSum / list.size)
    println("list end time ${System.currentTimeMillis()}")
}

参数默认值、位置参数、命名参数

kotlin允许函数的参数有默认值,在调用时就可以不写

fun printName(name: String = "Dean"){
	println("name is $name")
}

调用
printName("mm")
printName()

和dart差不多,可以指定参数传递给哪个,但是要注意位置参数,位置参数全部需要在第一个命名参数之前

fun printPerson(name: String = "Dean", age: Int = 2, sex: String = "male"){
	println("name is $name  , age is $age , sex is $sex")
}

调用时可以
printPerson()
printPerson("mm", age = 20)
printPerson(age = 20)

但是不可以
printPerson(age = 20, "mm")

原生字符串

和dart差不多,用“”“ 字符串 ”“”,包裹起来的字符串能够原封输出,可以加trimMargin来去除前面的空格

fun main(array: Array<String>){
    var text = """
        换行啊dadwadwj
        换行啊dawidawjoid
        换行啊dawidjaowd
    """.trimMargin("换行啊")
    println(text)
}

高阶函数和lambda

高阶函数:参数或者返回值为函数类型的函数

由于java不能把函数(方法)当成参数传递,所以一般都是使用接口来设置回调,比如

先定义一个接口,这种单抽象方法的接口叫做SAM接口(Single Abstract Methodpublic interface PlayListener{
	void onPlayerLoaded();
}

然后外面设置这个接口的实现类
public void setPlayListener(PlayListener listener){
	this.playListener = listener;
}

最后内部在需要的地方调用
if (playListener != null){
	playListener.onPlayerLoaded();
}

设置的时候使用lambda,但是java还是会创建匿名类对象
setPlayListener(() -> {
	
});

在kotlin中可以直接传递函数,所以这种使用SAM调用函数的方式就没必要了,但是写的时候可以通过lambda创建函数对象来传递

setPlayListener { println("onPlayerLoaded") }

在kotlin中可以通过三种方式来创建函数对象,创建好的函数对象可以被用作函数的参数或者函数的返回值

  • 双冒号加函数名
fun add(a: Int, b: Int) = a + b

已经有的函数加上::,就会被自动创建一个函数对象,是对象就可以被当成函数的参数和函数的返回值
val a = ::add
println(a(1, 2))

两种方式都可以产生相同的结果,但是实现方式不同,第一种直接调用函数,第二种是生成对象后通过invoke来调用正在的方法
println(add(1, 2))
println((::add)(1, 2))
  • 匿名函数
当函数直接赋值给变量,或者作为参数时,函数名称已经没有用了,kotlin里面强制不写,写了会报错
val a = fun(a: Int) = a + 1
println(a(1))
  • Lambda
    java中的lambda是纯为了简写,kotlin中增强了功能
上面设置监听时用的是  setPlayListener { println("onPlayerLoaded") }
看一步步衍变,首先可以写成上面说的匿名内部类,其中无返回值Unit可以省略
setPlayListener(fun(): Unit{
	println("aaaa")
})
用lambda简化, 由于没有参数->可以省略
setPlayListener({  ->
	println("aaaa")
})
当lambda是最后一个参数时可以放到括号外面, 没有参数胜率->
setPlayListener(){
	println("aaaa")
}
因为lambda是唯一参数,所以()又可以省略
setPlayListener{
	println("aaaa")
}
这时编译时,kotlin会通过上下文,即声明时setPlayListener内所写的参数,来自动判断使用lambda缩写之前的样子,比如参数和返回值

lambda中的return 是返回外层函数的,如果想要返回的是lambda,不需要写return,因为最后一行就是lambda的返回值,详情看这里

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值