文章目录
空指针防护
//main函数
fun main(args: Array<String>) {
//getContentLength(null) //报错:Null can not be a value of a non-null type String
getContentLength1(null)//不报错
}
fun getContentLength(content: String): Int {
return content.length
}
fun printUpperCase(content: String?) {
//?.表示如果不为空则执对象方法,下面是将字符串转换成大写,如果为空则不执行并返回null
println(content?.toUpperCase())
}
//参数后加?表示允许赋null
fun getContentLength1(content: String?): Int {
//return content.length //报错Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String?
//return content?.length//这里用?.是不行的,因为返回值是Int非空,当返回null时会类型不匹配
//解决办法1
return if (content == null) 0 else content.length
//解决办法2,?:表示优先前面表达式结果,当前面表达式结果为空是则取后面结果0
return content?.length ?: 0
//解决办法3,!!.强制去除非空检查,当你确信参数为非空时可以这样处理
return content!!.length
}
名称 | Java | Kotlin |
---|---|---|
对象 | MainActivity.this | this@MainActivity |
类 | MainActivity.class | MainActivity::class.java |
静态常量 | static final String text = “”; | const val text = “”(需要注意的是要把静态变量定义在类上方) |
比较类型 | if ("" instanceof String) {} | if ("" is String) {} |
比较字符串 | equals | ==(Kotlin 对字符串比较的写法进行优化了,其他类型对象对比还是要用 equals 方法) |
表达式
中缀表达式
中缀表达式
只有一个参数,且嗯infix修饰的函数,则可以去掉.() 来调用
例:
class Book{infix fun on(place:String}{...}
Book() on "desk"
分支表达式
- 每个分支的最后一行代码就是返回值
var i = if (content == null) 0 else content.length
- 也可以和分值表达式一样,最后一行代码就是返回值
val x: Int = 3;
val y: Int = 4
var z = 0
var i = try {
z = x/y
}catch (e: Exception){
z = 0
//finally无论如何都是会被执行到的
}finally {
println("程序执行完毕")
}
println(i)
扩展
- 扩展不能真正的修改他们所扩展的类,并没有在一个类中插入新成员或者方法
扩展函数
扩展函数作用域: 分发接收者类之中
示例一:
java 中实现
public class Utils {
public static float dp2px(int dpValue) {
return (0.5f + dpValue * Resources.getSystem().getDisplayMetrics().density);
}
}
float f = Utils.dp2px(100);
kotlin 中实现
fun Int.dp2px(): Float {
return (0.5f + this * Resources.getSystem().displayMetrics.density)
}
val dp2px = 100.dp2px()
示例二:
class Person(val name: String) {
fun eat() {Log.i(name, "I'm going to eat")}
fun sleep() {Log.i(name, "I'm going to sleep")}
}
新建一个文件 PersonExtensions.kt:
fun Person.cook() {Log.i("Person", "${this.name}: I'm going to cook")}
- 这个 this 指的就是调用这个拓展方法的当前 Person 对象
Person 扩展函数转为Java代码后如下:
@Metadata(
mv = {1, 1, 15},
bv = {1, 0, 3},
k = 2,
d1 = {"\u0000\f\n\u0000\n\u0002\u0010\u0002\n\u0002\u0018\u0002\n\u0000\u001a\n\u0010\u0000\u001a\u00020\u0001*\u00020\u0002¨\u0006\u0003"},
d2 = {"cook", "", "Lcom/chaochaowu/kotlinextension/Person;", "app_debug"}
)
public final class PersonExtensionsKt {
public static final void cook(@NotNull Person $this$cook) {
Intrinsics.checkParameterIsNotNull($this$cook, "$this$cook");
Log.i("Person", $this$cook.getName() + ": I'm going to cook");
}
}
java中调用如下:
PersonExtensionsKt.cook(new Person("Bob"));
扩展属性
- 扩展属性和扩展函数类似
class Person(val name: String) {
fun eat() {Log.i(name, "I'm going to eat")}
fun sleep() {Log.i(name, "I'm going to sleep")}
}
- 新建一个文件 PersonExtensions.kt:
const val currentYear = 2019
val Person.age: Int
get() = currentYear - this.birthdayYear
- 为 Person 增加年龄 age 的属性,但是不改 Person 类
lambda表达
lambda表达式简化过程
- 常规lambda表达式
button.setOnClickListener({v: View? -> println("")})
- lambda表达式是函数调用的 最后一个实参可以放到括号的外边
button.setOnClickListener(){v: View? -> println("")}
- lambda是函数 唯一的实参,还可以 去掉调用代码中的空括号对
button.setOnClickListener{v: View? -> println("")}
- 和局部变量一样,如果lambda参数的类型可以被推倒出来,不需要显示地指定
button.setOnClickListener{v -> println("")}
- 如果当前上下文期望的是 只有一个参数的 lambda ,并且这个参数的类型可以推断出来
button.setOnClickListener{println("")}
- 当形参和需要调用函数一致时
button.setOnClickListener{mainActivity::abc}
fun abc(v:View){}
button.setOnClickListener{::abc}//当在同一个类中其类名还可以省略
有形参和无形参的区别
//实现方法有形参:则不需要写接口名,且it代为实现方法形参
textView.setOnClickListener { it.viewTreeObserver}
// 无形参:需要注明实现接口
Thread(Runnable {})
Lambda表达式与函数
Lambda表达式释义
(Int, String) -> Unit
- Int, String 是形参
- Unit是函数返回类型,Unit代表返回的是空
Lambda表达作为变量
显示声明:
//有两个 Int 型参数和 Int 型返回值的函数
val sum : (Int, Int) -> Int = {x, y -> x + y}
//没有参数和返回值的函数
val action : () -> Unit = { println(42) }
当编译器可以推导出类型,可以隐式声明:
val sum = { x : Int, y : Int -> x + y }
val action = { println(42) }
Lambda表达的空类型
声明变量时的空类型
返回值为空
var canReturnNull : (Int, Int) -> Int? = { null }
变量为可空变量
// 将整个函数类型的定义包含在括号内并在括号后添加一个问号
var funOrNull : ((Int, Int) -> Int)? = null
函数形参的空类型
显示地检查 null:
fun foo(callback : (() -> Unit)?) {
if (callback != null) {
callback()
}
}
通过安全调用语法调用:
callback?.invoke() ?: /* 默认实现 */
Lambda 作为形参
示例一:
fun twoAndThree(operation: (Int, Int) -> Int) {
val result = operation(2, 3)
println("The result is $result")
}
fun main(args: Array<String>) {
twoAndThree { a, b -> a + b }
twoAndThree { a, b -> a * b }
}
//运行结果为:
>> The result is 5
>> The result is 6
示例二:
//过滤字符:
fun String.filter(predicate: (Char) -> Boolean): String {
val sb = StringBuilder()
for (index in 0 until length) {
val element = get(index)
if (predicate(element)) sb.append(element)
}
return sb.toString()
}
fun main(args: Array<String>) {
println("ab1c".filter { it in 'a'..'z' })
//println("ab1c".filter ({ c :Char -> c in 'a'..'z' }))
//println("ab1c".filter { c :Char -> c in 'a'..'z' })
//println("ab1c".filter { c -> c in 'a'..'z' })
//println("ab1c".filter { it in 'a'..'z' })
}
//运行结果:
>> abc
Lambda 作为形参的默认值
fun <T> Collection<T>.joinToString(
separator: String = ", ",
prefix: String = "",
postfix: String = "",
//为函数类型的参数提供默认值。
transform: (T) -> String = { it.toString() }
): String {
val result = StringBuilder(prefix)
for ((index, element) in this.withIndex()) {
if (index > 0) result.append(separator)
//调用传入的函数。
result.append(transform(element))
}
result.append(postfix)
return result.toString()
}
fun main(args: Array<String>) {
val letters = listOf("Alpha", "Beta")
println(letters.joinToString())
println(letters.joinToString { it.toLowerCase() })
println(letters.joinToString(separator = "! ", postfix = "! ",
transform = { it.toUpperCase() }))
}
//运行结果为:
>> Alpha, Beta
>> alpha, beta
>> ALPHA! BETA!
Lambda 作为函数返回值
//声明一个枚举类型。
enum class Delivery { STANDARD, EXPIRED }
class Order(val itemCount : Int)
//返回的函数类型为:形参为 Order 类,返回类型为 Double。
fun getShippingCalculator(delivery : Delivery) : (Order) -> Double {
if (delivery == Delivery.EXPIRED) {
return { order -> 6 + 2.1 * order.itemCount }
}
// Lamabda实现方式
return { order -> 1.2 * order.itemCount }
// 匿名函数实现方式
// return fun(order:Order):Double{return 1.2 * order.itemCount}
}
fun main(args: Array<String>) {
val calculator = getShippingCalculator(Delivery.EXPIRED)
println("cost ${calculator(Order(3))}")
}
Lambda 作为形参在java中使用
kotlin函数:
fun twoAndThree(operation: (Int, Int) -> Int) {
val result = operation(2, 3)
println("The result is $result")
}
在java中实现:
Customer.asd asd = new Customer.asd();
asd.twoAndThree(new Function2<Integer, Integer, Integer>() {
@Override
public Integer invoke(Integer integer, Integer integer2) {
return integer+integer2;
}
});
简化写法:
Customer.asd asd = new Customer.asd();
asd.twoAndThree((integer, integer2) -> integer+integer2);
注:在java中就通过Function实现,分别有Function,Function2,Function3分别代表一个形参,两个形参,三个形参,每个的最后一个为返回类型
扩展函数在Java中使用
List<String> list = new ArrayList();
list.add("42");
CollectionsKt.forEach(list, s -> {
System.out.println(s);
retrun Unit.INSTANCE;
});
Lambda表达式for循环中局部返回
使用标签返回
- 类似于for循环中的break表达式
data class Person(val name: String, val age: Int)
val people = listOf(Person("Alice", 29), Person("Bob", 31))
fun lookForAlice(people: List<Person>) {
people.forEach label@{
if (it.name == "Alice") return@label
}
println("Alice might be somewhere")
}
fun main(args: Array<String>) {
lookForAlice(people)
}
运行结果为:
>> Alice might be somewhere
匿名函数方式局部返回
data class Person(val name: String, val age: Int)
val people = listOf(Person("Alice", 29), Person("Bob", 31))
fun lookForAlice(people: List<Person>) {//这种方式是在forEach后面的括号里
people.forEach(fun (person) {
if (person.name == "Alice") return
println("${person.name} is not Alice")
})
}
fun main(args: Array<String>) {
lookForAlice(people)
}
运行结果为:
>> Bob is not Alice
- 匿名函数和普通函数有相同的指定返回值类型的规则,代码块匿名函数 需要显示地指定返回类型,如果使用 表达式函数体,就可以省略返回类型。
注意:
- lambda表达式没有使用fun关键字,所以lambda中的return从最外层的函数返回。
- 匿名函数使用了fun,因此return表达式从匿名函数返回。