1.lateinit 和 by lazy
lazyinit和lazy用于在kotlin中延迟初始化,lateinit用于变量var,lazy用于常量val,形如这种形式:
private lateinit var name: String
private val pwd: String by lazy { "123456" }
注意,lazy第一次调用会执行整个lambda表达式,以后调用只返回之前的结果,仔细一品,有点单例的味道,测试代码:
private val pwd: String by lazy {
println("----- 我执行了")
"123456"
}
执行:
fun main() {
println(pwd)
println(pwd)
}
结果:
2.默认参数
kotlin可以给方法指定默认参数,如果指定了默认参数,调用方法时可以不传入此参数,但其余参数需要指定关键字,未指定参数取默认参数,形如:
fun getStudentInfo(
name: String = "张三",
age: Int = 20,
address: String = "江苏南京",
sex: String = "男"
): String {
return "$name,$age,$sex,$address"
}
调用:
fun main() {
val result = getStudentInfo(age = 19)
println(result)
}
运行结果:
3.data class
kotlin中的数据类,定义非常简单在class前面加个data关键字,然后构造函数中指定变量即可,形如:
data class CityInfo(val name: String, val location: String)
对应于java中:
class CityInfo {
private String name;
private String location;
public CityInfo(String name, String location) {
this.name = name;
this.location = location;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
}
4.打印日志
kotlin中打印日志采用$关键字,形如:
val name = "张三"
println("你好,我是$name")
val a = 1
val b = 10
println("a+b=${a + b}")
结果:
5.单例
简单列举几种写法:
//object修饰的单例
object CityTest
//懒汉式 @Synchronized保证线程安全
class CityTest1 {
companion object {
private var instance: CityTest1? = null
get() {
if (null == field) {
field = CityTest1()
}
return field
}
@Synchronized
fun get(): CityTest1 {
return instance!!
}
}
}
//lazy特性,非首次调用只返回结果
class CityTest2 {
companion object {
val instance: CityTest2 by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
CityTest2()
}
}
}
//静态内部类的方式
class CityTest3 {
companion object {
val instance = SingletonHolder.holder
}
private object SingletonHolder {
val holder = CityTest3()
}
}
调用测试:
fun main() {
println(CityTest)
println(CityTest)
println(CityTest1.get())
println(CityTest1.get())
println(CityTest2.instance)
println(CityTest2.instance)
println(CityTest3.instance)
println(CityTest3.instance)
}
结果:
6.省略findviewById
只需要在build.gradle中添加kotlin-android-extensions
插件即可,形如:
plugins {
···
id 'kotlin-android-extensions'
···
}
activity中导入import kotlinx.android.synthetic.main.activity_main.*
包,然后就可以直接使用xml中的id作为组件的变量名了:
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//直接使用即可
btnTest.setOnClickListener {
}
}
}
7.扩展函数和扩展属性
kotlin中,扩展函数和扩展属性的含义就是可以给指定类新增属性和方法,比如有些第三方jar中的一些类,我们无法直接修改,可以采用扩展的方式增加属性和方法,方便开发。
下面介绍一下使用方式,其实很简单,类名【.】名字(属性名或者方法名)即可,先看下扩展函数的例子,给Int值新增一个扩展函数,调用后自动加上[]
,代码如下:
fun Int.toSpString(): String {
return "[$this]"
}
测试代码:
fun main() {
println(2.toSpString())
}
结果:
例子非常简单,抛砖引玉,大家可以自行发散思维,扩展函数还是挺方便的,扩展属性也类似,比如:
private val String.upper
get() = this.toUpperCase()
大小写转换,运行测试:
fun main() {
println("abc".upper)
}
结果:
注意:对于普通变量,初始化给定的值后将直接写入后端域变量中(一般是field
),但扩展属性因为没有后端域变量,所以不应该初始化。
再举个在Android中使用扩展函数的例子:
//比如平时我们加载图片,可以使用扩展函数封装一下,方便使用
fun ImageView.loadImage(url: String) {
Glide.with(context).load(url).into(this)
}
//加载图片直接这样用
imageView.loadImage(url)
再比如,dp值转像素值px,此处为float添加扩展属性dp:
//dp值转像素值px
val Float.dp: Float
get() = android.util.TypedValue.applyDimension(
android.util.TypedValue.COMPLEX_UNIT_DIP, this, Resources.getSystem().displayMetrics
)
调用:
val width = 20f.dp
Log.e("width","width = $width")
结果:
03-23 10:03:48.069 7594-7594/com.kotlindemo E/width: width = 26.625002
8.Kotlin标准库中的函数
这里盗用下网上的对比图,仅学习用,勿怪,当然推荐大家可以对每个函数进行代码测试,印象更深刻,不然容易混淆。
测试代码如下,重点关注返回值和内部使用关键字(it和this):
fun testMethod() {
val runResult = 1.run {
println("run : $this")
100
}
println("runResult : $runResult")
val applyResult = 1.apply {
println("apply : $this")
}
println("applyResult : $applyResult")
val alsoResult = 1.also {
println("also : $it")
}
println("alsoResult : $alsoResult")
val letResult = 1.let {
println("let : $it")
100
}
println("letResult : $letResult")
val withResult = with(1) {
println("with : $this")
100
}
println("withResult : $withResult")
}
打印结果:
当然除了这几个标准函数之外,还有repeat
,takeIf
,takeUnless
等,大家可以自行尝试,基本上都是字面意思。
9.Kotlin高阶函数
其实没那么高端,简单来说就是一个可以接收函数作为函数参数的函数,听我这么一说是不是感觉更乱了,不要慌,我们一步步来,我们先定义一个简单的高阶函数:
//把()->Unit 当作是一种函数类型,method就是函数类型的变量名
fun doSomething(method: () -> Unit) {
method.invoke()
}
()->Unit
这个东西表示doSomething
方法接收一个无参,无返回值的函数作为自己的参数,invoke()
方法就是调用传入的函数,当然你也可以这样调用method()
,我们测试一下:
fun main() {
doSomething {
println("我是高阶函数的lambda类型的参数")
}
}
运行:
是不是挺简单的,你可能会说这个doSomething
后面怎么直接是个{}
啊,其实你也可以这么写:
doSomething(
fun() {
println("我是高阶函数的lambda类型的参数")
}
)
这样可能更容易理解,实际代码还是用lambda
推荐的语法,这样写更简便,下面我们在上面的基础上增加参数和返回值,如下:
fun doSomething(param: Int, method: (Int) -> String) {
val result = method(param)
println(result)
}
是不是很简单,在()
中加入参数类型,->
后面是函数的返回值类型,既然我们给高阶函数增加了Int
类型的参数,那么调用method
方法时这个参数哪里来呢,当然我们主动给它指定就行了啊,所以新增了Int
类型的param
参数,看下如何使用:
fun main() {
doSomething(100) {
val result = it * 2
return@doSomething "result :$result"
}
}
传入相关参数即可,这里传入100,做了简单的运算后打印该结果:
下面我们给函数doSomething增加个String类型的返回值,如下:
fun doSomething(param: Int, method: (Int) -> String): String {
return method(param)
}
fun main() {
val result = doSomething(100) {
val result = it * 2
return@doSomething "result :$result"
}
println(result)
}
运行结果和上面一样,我们接收了doSomething方法的返回值并打印:
我要是需要添加两个参数怎么办?那就接着添加呗:
fun doSomething(param: Int, str: String, method: (Int, String) -> String): String {
return method(param, str)
}
然后调用的时候要注意下,a, b ->
这个a
代表第一个参数,b
代表第二个参数,当然你可以随便起名字都可以的,如果你用不到的参数还可以用_
代替:
fun main() {
val result = doSomething(100, "调用我了") { a, b ->
val result = a * 2
return@doSomething "$result -- $b"
}
println(result)
}
结果:
我们再进阶下,把所有的参数类型和返回值类型都改为泛型T
,试试看:
fun <T> doKotlin(param: T, param2: T, method: (T, T) -> T): T {
return method(param, param2)
}
我们指定泛型可以代指任何类型,我们调用试试:
fun main() {
val result = doKotlin(1, 2) { a, b ->
return@doKotlin a + b
}
println(result)
}
结果:
一切正常,那么我们如果要参数类型不同呢,简单,再加个泛型不就行了:
fun <T, R> doKotlin2(param: T, param2: R, method: (T, R) -> R): R {
return method(param, param2)
}
又加了个泛型R,调用:
fun main() {
val result = doKotlin2("张三", true) { a, _ ->
return@doKotlin2 a == "张三"
}
println(result)
}
结果:
我们再修改下,上面我们介绍了扩展函数,那么如果我们给泛型加上扩展函数,是不是所有类型都可以使用该高阶函数了呢,试试:
fun <T, R> T.doAndroid(method: (T) -> R): R {
return method(this)
}
我们定义了泛型T的扩展函数doAndroid
,method
接收的参数就是当前类的实例对象,那么我们调用下试试:
fun main() {
val result = "Hello".doAndroid {
"100"
}
println(result)
}
没啥毛病,那么到这里你是不是有种似曾相识的感觉,如果没有的话,点开kotlin
中标准库中的函数,去看看源码,比如let
源码:
@kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block(this)
}
inline
忽略,是不是就是和let
源码几乎一致,其实不止let
,像apply
,run
等函数也都是高阶函数的封装,到这里基本上应该可以理解高阶函数的内涵了,代码自己跟着敲下,加深印象,另外熟悉下lambda
语法,就基本可以掌握了。
看下apply
函数的源码:
@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block()
return this
}
发现也是如此,但是你可能会看到T.()
,这又是什么鬼呢,既然是T.那么肯定跑不了扩展函数的范畴,但是具体是啥咱也不知道,可以理解为匿名扩展函数,这样在使用的时候在代码块中就可以使用this代指当前对象,这也正是扩展函数所具有的特性:
如果不太理解,可以看看这篇文章!
先到这,后续有需要记录的再来补充!