荐 Kotlin到底比Java好在哪,我们不得不说的事
Kotlin优势
kotlin更安全
这是它最重要的优势,空引用由类型系统控制,你不会再遇到NullPointerException。这个空指针异常就像一个隐藏的定时炸弹,指不定啥时候就炸了。
在Kotlin中调用Java代码,Java代码中需要使用上非空注解。
代码简洁
使用Lambda 表达式,大量节省末班代码,特别是重复多余的findViewById。都说越少的代码越能减少错误。
函数式支持
使用高阶函数,可以将其作为返回值或参数值使用,特别方便。
什么是高阶函数呢?就是以函数作为参数,又以函数作为返回值就是高阶函数,而每个函数又对应着Lambda表达式。
常见的高阶函数map()、forEach()、flatMap()、reduce()、fold()、filter()、takeWhile()、let()、apply()
扩展函数
Kotlin同C#类似,能够扩展一个类的新功能而无需继承该类或使用像装饰者这样的任何类型的设计模式。Kotlin支持扩展函数和扩展属性。
1) 表达式可以直接作为函数返回值
2)扩展函数: fun 类名.扩展方法名(参数列表){方法体} 或者 fun 类名.扩展方法名(参数列表) = 表达式
fun MyClass.add(a:Int,b:Int) = a+b
3)扩展属性:var 类名.扩展属性 / 对应其他方法
var MyClass.d: String
get() = "sdasda"
set(value){d = value}//MyClass的扩展成员变量d
Kotlin中没有静态成员
对应的解决方案是伴生类
与java可互相调用
Kotlin的标准库更多的是对Java库的扩展,但是在Kotlin中可以使用Android库。在Kotlin和Java代码中可以互相调用,完美无障碍。
与Java区别
- 基本类型类似,不在存在拆箱装箱过程了
- 变量的声明只有var可变变量和val只读变量,Kotlin 能自动聪明的判断变量的数据类型
- Java中支持char>int >long自动转化,为了降低不必要的错误Kotlin中不支持
- Java中的原始类是Object,而Kotlin中确实Any,Any方法中只有equals(),hashCode,toString三个方法。
- Java当中的类型转换是指存在继承关系的,子类可以向上自动转型为父类,而父类转子类则需要强制转换。而Kotlin中的类型转换是使用as关键字
- Java中存在多个相同类名,引用需要用包名.类名,在Kotlin中则使用如下
import A.Student as AS//将A包下的Student类取外号为"AS"
import B.Studnet as BS//将B包下的Student类取外号为"BS"
- Kotlin有独特的区间类,名字叫做Range。用整型区间举例:
var intRange:IntRange = 0..1024//区间为[0,1024],注意是全闭的
var intRange2:IntRange = 0 until 1024//区间为[0,1024),注意是半闭半开的,包头不包尾
println("50是否存在与区间[0,1024]:"+intRange.contains(50))
println("-3是否存在与区间[0,1024):"+intRange.contains(-3))
//遍历区间
for(i in intRange){
println(i)
}
-
Kotlin中一般声明数组开始用intArrayOf和arrayOf来声明
-
Kotlin的权限修饰符没有了default,对应的改成了internal
-
在Kotlin中使用"“等价Java 中的equals()方法。使用”=“来等价Java中的”==",比较两个引用是否是同一个对象。
-
Kotlin中没有了Switch语句,用when来替代,它更加强大,它的判断值可以是表达式。
-
Kotlin中构造器需要使用constructor这个关键字,或者有个特殊默认的构造方式,这种叫做主构造函数
class Student(var name:String,var age:Int)//构造器的特殊写法
-
Kotlin中从属于类的函数叫做"成员方法",不从属于类的叫做函数,如一下参数是以方法块的形式表现,这个就是"函数"
-
Kotlin的默认访问权限是public,而非Java中默认的访问权限default。
final:是Kotlin的默认修饰符。如果你想你的类能被继承你需要使用open来修饰。
data:这个修饰符很好用,也很有特色。用data修饰后:
1)会自动将所有成员用operator声明,自动为成员生成getter和setter。
2)自动重写了equals()/hashCode()和toString()方法
3)data类没有无参构造函数,这感觉是个坑,所以Kotlin官方给出了解决方案,就是使用NoArg(无参构造函数)和AllOpen(可被继承)来解决问题。 -
在Kotlin的接口中可以实现方法不报错,如下在Java中会报错,但是在Kotlin中不会报错。
interface JieKou{
fun A(){
//方法体a
println("接口里面的方法,我是方法体")
}
}
- 在class的内部类中,需要个加上inner 这个关键字,否则这个类默认是静态的。非静态内部类的this关键字冲突可以使用"this@外部类类名"来区分。
var ab = A.AB() //实例化A类中的静态内部类AB
var ac = A().AC() //实例化A类中的非静态内部类AC
A a=new A();
A.AC ac =a.new AC();
A.AC ac=new A.AC();
- 包级函数,在Kotlin 的类名之外的函数,为之包级函数。包级函数的所有函数(包括扩展函数)都是静态的,在编译的时候呢,Kotlin就把所有的包级函数都放在了一个类当中,Java可以通过此类名来访问到所有的包级函数,如Test.getMessage()
//文件名 Test.kt
class Student(var name:String,var age:Int){
}
fun getMessage(){//这个扩展方法被编译Test.kt中的静态方法public static getMessage(Student student)
println("")
}
- Kotlin中的通配符不是“?”,而是" * “。”?"被拿来用于判断非空了
- 正则表达式,除了Java中传统的方式外,还有新的方式就是使用
var results = Regex(geShi).split(str)//Regex为Kotlin中处理正则表达式的工具类
- IO流中使用"use"自动关闭读写文件
伴生类
这个特性我需要单独来讲解说明,在Kotlin中没有static,我们在伴生类中的变量和方法相当于静态变量和静态方法。
companion object {//静态方法和变量都应该写在这里面
var a:Int = 3;//静态变量
fun get():Int{
return a
}
}
Kotlin中的几种注解
1.@JavaFiled:将属性编译为Java变量
2.@JvmStatic:将对象的方法编译成Java静态方法,通常应用与伴生对象
3.@JvmOverloads:默认参数生成重载方法
4.@file:JvmName:指定Kotlin文件编译后的类名
内联函数Let和Apply
let
let扩展函数的实际上是一个作用域函数,当你需要去定义一个变量在一个特定的作用域范围内,let函数的是一个不错的选择;let函数另一个作用就是可以避免写一些判断null的操作。
//另一种用途 判断object为null的操作
object?.let{//表示object不为null的条件下,才会去执行let函数体
it.todo()
}
它的使用场景如下:
场景一: 最常用的场景就是使用let函数处理需要针对一个可null的对象统一做判空处理。
场景二: 然后就是需要去明确一个变量所处特定的作用域范围内可以使用
with
with函数和前面的几个函数使用方式略有不同,因为它不是以扩展的形式存在的。它是将某对象作为函数的参数,在函数块内可以通过 this 指代该对象。返回值为函数块的最后一行或指定return表达式。
如下两代码是等价的:
val result = with(user, {
println("my name is $name, I am $age years old, my phone number is $phoneNum")
1000
})
val result = with(user) {
println("my name is $name, I am $age years old, my phone number is $phoneNum")
1000
}
使用场景
适用于调用同一个类的多个方法时,可以省去类名重复,直接调用类的方法即可,经常用于Android中RecyclerView中onBinderViewHolder中,数据model的属性映射到UI上。
override fun onBindViewHolder(holder: ViewHolder, position: Int){
val item = getItem(position)?: return
with(item){
holder.tvNewsTitle.text = StringUtils.trimToEmpty(titleEn)
holder.tvNewsSummary.text = StringUtils.trimToEmpty(summary)
holder.tvExtraInf.text = "难度:$gradeInfo | 单词数:$length | 读后感: $numReviews"
...
}
}
apply
从结构上来看apply函数和run函数很像,唯一不同点就是它们各自返回的值不一样,run函数是以闭包形式返回最后一行代码的值,而apply函数的返回的是传入对象的本身。
以下与with函数不一样,其结果值result指代的是user
fun main() {
val user = User("Kotlin", 1, "1111111")
val result = user.apply {
println("my name is $name, I am $age years old, my phone number is $phoneNum")
1000
}
println("result: $result")
}
使用场景
1)apply一般用于一个对象实例初始化的时候,需要对对象中的属性进行赋值。或者动态inflate出一个XML的View的时候需要给View绑定数据也会用到,这种情景非常常见。
mSheetDialogView = View.inflate(activity, R.layout.biz_exam_plan_layout_sheet_inner, null).apply{
course_comment_tv_label.paint.isFakeBoldText = true
course_comment_tv_score.paint.isFakeBoldText = true
course_comment_tv_cancel.paint.isFakeBoldText = true
course_comment_tv_confirm.paint.isFakeBoldText = true
course_comment_seek_bar.max = 10
course_comment_seek_bar.progress = 0
}
2) 也和let函数一样用于去进行判null。
本文地址:https://blog.csdn.net/luo_boke/article/details/107172965