Kotlin -特殊用法详解
Posted wzgiceman
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kotlin -特殊用法详解相关的知识,希望对你有一定的参考价值。
本文适合已经了解Kotlin基本用法,想更加深入的使用Kotlin方法和性能的同学们
如果你是Kotlin小白,可以先了解一下Kotlin的基础用法
解构声明
我们在一个文件中定义一个Person类
data class Person(val name: String, val age: Int)
我们可以通过简单的语法获得这个类的name和age属性
val (name, age) = Person("tom", 11)
println(name)
println(age)
上面的Person是数据类(data class)当我们定义一个普通的类,不能简单的通过下面的方法获取name和age的值
class Dog(val name: String, val age: Int)
val (nameDog, ageDog) = Dog("James", 5)
为什么数据类可以,普通类就不可以呢? 因为数据类帮我们实现了解构声明需要的componentN方法这个N可以是1或者2等.
标准库中的let, takeIf, takeUnless,apply, also,with,run方法
let
let方法接收一个参数,参数是个方法。这个方法的名字叫block。let接收的方法是一个穿入值是T类型返回值是R类型的方法。并且我们知道这个方法的反回值就是整个let方法的返回值。绕口,见谅
// Java
if (message != null)
System.out.println(message)
// Kotlin 如果message是null下面这行忽略
message?.let println(it)
takeIf
定义
/**
* Returns `this` value if it satisfies the given [predicate] or `null`, if it doesn't.
*/
@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? = if (predicate(this)) this else null
- takeIf是个扩张方法
- 参数是个方法,(T) -> Boolean,这个方法的参数是类型T的对象,返回值是一个布尔类型
- if (predicate(this)) this else null,这个是方法体。通过predicate这个函数为真,返回对象T,要么返回null
举例
val result = "Hello World".takeIf
it.length > 1
println(result)
//打印结果System.out: Hello World
takeUnless
定义上看,takeIf和takeUnless只是在方法体中的if的判断相反,其他都一样。
/**
* Returns `this` value if it _does not_ satisfy the given [predicate] or `null`, if it does.
*/
@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.takeUnless(predicate: (T) -> Boolean): T? = if (!predicate(this)) this else null
apply
apply函数是这样的,调用某对象的apply函数,在函数范围内,可以任意调用该对象的任意方法,并返回该对象
fun testApply()
// fun <T> T.apply(f: T.() -> Unit): T f(); return this
ArrayList<String>().apply
add("testApply")
add("testApply")
add("testApply")
println("this = " + this)
.let println(it)
// 运行结果
// this = [testApply, testApply, testApply]
// [testApply, testApply, testApply]
编译过后的class文件
public static final void testApply()
ArrayList localArrayList1 = new ArrayList();
ArrayList localArrayList2 = (ArrayList)localArrayList1;
int $i$a$1$apply;
ArrayList $receiver;
$receiver.add("testApply");
$receiver.add("testApply");
$receiver.add("testApply");
String str = "this = " + $receiver;
System.out.println(str);
localArrayList1 = localArrayList1;
ArrayList it = (ArrayList)localArrayList1;
int $i$a$2$let;
System.out.println(it);
also
/**
* Calls the specified function [block] with `this` value as its argument and returns `this` value.
*/
@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.also(block: (T) -> Unit): T block(this); return this
also的定义跟apply很像。接收一个函数,函数的参数是类型T的对象。also扩展方法的返回值也是类型T的对象。
举例
val result = "Hello World".also
println(this)
println(it)
println(result)
//结果
//System.out: com.example.kotlintest.MainActivity@ba61275
//System.out: Hello World
//System.out: Hello World
在实际开发中我们对TextView对象设置属性的时候我们可以用also扩展方法
run
run函数和apply函数很像,只不过run函数是使用最后一行的返回,apply返回当前自己的对象。
with
with函数是一个单独的函数,并不是Kotlin中的extension,所以调用方式有点不一样,返回是最后一行,然后可以直接调用对象的方法,感觉像是let和apply的结合。
代码示例:
fun testWith()
// fun <T, R> with(receiver: T, f: T.() -> R): R = receiver.f()
with(ArrayList<String>())
add("testWith")
add("testWith")
add("testWith")
println("this = " + this)
.let println(it)
// 运行结果
// this = [testWith, testWith, testWith]
// kotlin.Unit
class文件
public static final void testWith()
Object localObject = new ArrayList();
ArrayList localArrayList1 = (ArrayList)localObject;
int $i$a$1$with;
ArrayList $receiver;
$receiver.add("testWith");
$receiver.add("testWith");
$receiver.add("testWith");
String str = "this = " + $receiver;
System.out.println(str);
localObject = Unit.INSTANCE;
Unit it = (Unit)localObject;
int $i$a$2$let;
System.out.println(it);
流程控制
if else
跟Java中的if else完全一样,就是分支选择,不同于Java,可以赋值(类似于java中等?:处理)
val age = 20
val personType = if (age <= 12)
"Child"
else if (age >= 13 && age <= 17)
"Teenager"
else
"Adult"
println(personType)
//输出Teenager
when
when相当于Java中swich,不同于java优复制的用法
val x = "Batman"
// If "when" is used as an expression then it MUST be exhaustive.
val hasPrefix = when (x)
is String -> x.startsWith("Bat")
else -> false
hasPrefix
for
循环处理,不同于java有更方便强大的处理方法
val list = ArrayList<String>()
permissions.forEach
if (ContextCompat.checkSelfPermission(activity, it) != PackageManager.PERMISSION_GRANTED)
list.add(it)
permissions.forEachIndexed postion, it ->
Log.e("postion", postion.toString())
Log.e("item", it)
中缀表示法
infix
标有 infix 关键字的函数也可以使用中缀表示法(忽略该调用的点与圆括号)调用。中缀函数必须满足以下要求:
- 它们必须是成员函数或扩展函数;
- 它们必须只有一个参数;
- 其参数不得接受可变数量的参数且不能有默认值。
class MyStringCollection
infix fun add(s: String) ……
fun build()
this add "abc" // 正确
add("abc") // 正确
add "abc" // 错误:必须指定接收者
默认/命名参数
可以在调用函数时使用命名的函数参数。当一个函数有大量的参数或默认参数时这会非常方便
fun reformat(str: String,
normalizeCase: Boolean = true,
upperCaseFirstLetter: Boolean = true,
divideByCamelHumps: Boolean = false,
wordSeparator: Char = ' ')
……
我们可以使用默认参数来调用它:
reformat(str)
reformat(str, true, true, false, '_')
reformat(str, wordSeparator = '_')
?表达式和Elvis表达式
Kotlin特有的?表达式和Elvis表达式可以在确保安全的情况下,写出更加简洁的代码。比如我们在android页面开发中常见的删除子控件操作,用Java来写是这样的
if(view!=null)
if(view.getParent()!=null)
if(view.getParent() instanceof ViewGroup)
((ViewGroup)view.getParent()).removeView(view);
为了获得更加安全的代码,我们不得不加上很多if else 判断语句,来确保不会产生空指针异常。但Kotlin的?操作符可以非常简洁地实现上述逻辑
(view?.parent as? ViewGroup)?.removeView(view)
那么这个?表达式的内在逻辑是什么呢?以上述代码为例,若view == null,则后续调用均不会走到,整个表达式直接返回null,也不会抛出异常。也就是说,?表达式中,只要某个操作对象为null,则整个表达式直接返回null。
除了?表达式,Kotlin还有个大杀器叫Elvis表达式,即?: 表达式,这两个表达式加在一起可以以超简洁的形式表述一个复杂逻辑。
val v=A?.b ?: C
以上面表达式为例,我们以?:把它划分成两个部分。若前面部分为null,则整个表达式返回值等于c的值,否则等于前面部分的值
更简洁的字符串
同Java一样,Kotlin也可以用字面量对字符串对象进行初始化,但Kotlin有个特别的地方是使用了三引号”””来方便长篇字符串的书写。而且这种方法还不需要使用转义符。做到了字符串的所见即所得
val name="""
aaa
bbb
cccc
"""
同时,Kotlin还引入了字符串模板,可以在字符串中直接访问变量和使用表达式
val name="""
aaa
bbb
cccc
"""
AbLogUtil.e("--->$name")
对象比较
Java的 == 操作符是比较引用值,但Kotlin 的 == 操作符是比较内容, === 才是比较引用值。基于这点,我们可以写出逻辑更简洁合理的代码
关键字object
Kotlin中一切皆为对象,object在Kotlin中是一个关键字,笼统来说是代表“对象”,在不同场景中有不同用法。
1.对象表达式,可以直接创建一个继承自某个(或某些)类型的匿名类的对象,而无须先创建这个对象的类。这一点跟Java是类似的:
httpManager.doHttpDeal(api).observeOn(Schedulers.io()).map
return@map true
.observeOn(AndroidSchedulers.mainThread()).subscribe(object : Subscriber<Boolean>()
override fun onNext(t: Boolean?)
override fun onCompleted()
override fun onError(e: Throwable?)
)
2.对象字面量。这个特性将数字字面量,字符串字面量扩展到一般性对象中了。对应的场景是如果我们只需要“一个对象而已”,并不需要特殊超类型。典型的场景是在某些地方,比如函数内部,我们需要零碎地使用一些一次性的对象时,非常有用。
var user=object
var sex:Int=0
var year:Int=1
3.对象声明。这个特性类似于Java中的单例模式,但我们不需要写单例模式的样板代码即可以实现。
object PictureCompression
private fun initialize(context: Context)
PATH.initialize(context)
private val temPictureFile by lazy
File(PATH.imageSaveDir)
请注意上述代码是声明了一个对象,而不是类,而我们想要使用这个对象,直接引用其名称即可
有趣的冒号
从语法上来看,Kotlin大量使用了冒号(:)这一符号,我们可以总结一下,这个冒号在Kotlin中究竟代表什么。
-
在变量定义中,代表变量的类型
-
在类定义中,代表基类的类型
-
在函数定义中,代表函数返回值的类型
-
在匿名对象中,代表对象的类型
函数类型
Kotlin中一切皆是对象,函数也不例外。在Kotlin中,函数本身也是对象,可以拥有类型并实例化。Kotlin 使用类似 (Int) -> String 的一系列函数类型来处理函数的声明,比如我们常见的点击回调函数:
val onClick:(View)->Unit=
了解了Kotlin的方法,下面用java回掉来举例学习,kotlin的回调与java在写法上略有不同,直接上代码
1、生成Person类及MyInterface 接口
class Person
val name:String = "Person"
lateinit var mListen: MyInterface //接口可以延时加载
fun setListeren(listen: MyInterface)
this.mListen = listen
this.mListen?.poo(" poo :" +name)
interface MyInterface
fun poo(str: String)
2、在Main或Activity中回调
fun main(args: Array<String>)
println("Hello, world!")
var person = Person()
person.setListeren(object : Person.MyInterface //object的作用是调用内部匿名类
override fun poo(str:String)
println(str)
)
3、打印结果
Hello, world!
poo :Person
以上是用原java写法来实现kotlin回调的,目的是从java转来的人好理解,看看kotlin是如何简化java代码的
class Person
val name:String = "Person"
lateinit var mListen: (String) -> Unit // 声明mListen是一个函数(单方法接口),入参String,无返回值
fun setListeren(listener: (String) -> Unit)
this.mListen = listener
this.mListen("invoke :" +name) //等于 mListen?.invoke("invoke :" +name) X()等同于X.invoke()
//不再需要声明接口类!
fun main(args: Array<String>)
println("Hello, world!")
var person = Person()
person.setListeren println(it) // 只有一个参数的简化结果 it代表入参 String类
打印结果
Hello, world!
invoke :Person
以上是关于Kotlin -特殊用法详解的主要内容,如果未能解决你的问题,请参考以下文章
KotlinKotlin 语言简介 ( Kotlin 语言发展 | Kotlin 语言与 Java 虚拟机 | Java 与 Kotlin 联系 | Kotlin 跨平台特性 )