前言
小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
Java和Kotlin都是Android的官方开发语言,但是Kotlin已经上升为第一开发语言,有过之而无不及。
如今,Java 17 都已经出来了,自 Java 8 之后语法改进变化很大,这些改变无不体现着新式语法,很多语法糖层出不穷,从初看别扭、不熟悉到熟练运用之后发现真香,这个掌握新式语法过程并不困难,因为语言都是万变不离其宗的,只要掌握一门,在学另外一门时会有连锁联想,理解起来会更快。
今天就来聊聊这两种语言和区别与联系,并带领大家一起尝尝 Kotlin 新式语法糖,学习它不仅能帮助你领略这门语言本身的魅力,还能透过它在学习 Java 新特性时举一反三。
联系与区别
联系
Kotlin在底层与java完全兼容- 编译的产物也是
java的class文件 - 都可通过虚拟机运行
Kotlin 简直是一体两面、无缝结合啊!
区别
- 用
Kotlin编写的程序可以做到不依赖虚拟机运行,称为Native(原生)方式 - 比在虚拟机上运行速度更快,对移动设备来说意义重大
Kotlin代表未来的开发方向也不算夸张,17 年推出站在了前人的肩膀上(比如TS),和现在新出的编程语言的语法规则几乎完全一样
新式语法特征
不用分号
System.out.println("Hello World!");
println("Hello World!")
只有想在一行内写多条语句时,才需要用分号将每个语句隔开,见下:
println("Hello World!");println("HUALEI");println("Hansome boy!")
变量和常量用关键字去区分开来,并且数据类型放在 : 冒号的后面
// 定义一个常量,类型根据赋值自动推断
val param = 1
// 定义一个变量
var param:Int = 2
- 方法或函数的返回值类型放在
:冒号的后面,例如:
fun onOptions():Boolean{
return true
} // 其中boolean就是函数的返回值类型
不再完全忠诚于面向对象
支持全局函数,既可以在类外面定义函数, 也可以把函数保存在变量中,还可以定义函数类型,跟 C/C++ 一样
支持 lambda 表达式,语法精简再精简
// 三个整数相加
val threeSum:(Int,Int,Int) -> Int = {a,b,c -> a+b+c}
println(threeSum(1,2,3)) // 6
定义变量时可以省略数据类型,编译器能够自动识别数据类型
注意:
- 编译器可以根据其他内容推导出来,否则不能省略哦~
可以在字符串中嵌入表达式(使用 ${} 的形式嵌入表达式),比字符串的格式化函数更方便
java中使用字符串格式化函数:
String name = "帅哥";
String hello = String.format("Hi %s!",name) // Hi 帅哥!
kotlin中则是:
val name = "帅哥"
val hello = "Hi ${name}!" // Hi 帅哥!
将可为空和不可为空作为两种数据类型对待,Kotlin 中所有参数变量都是不可为空的,除非你在这个类型/变量后面加上 ?
// 类型后加一个 ? 表示可以为空
val age:String ?= null
// 如果不加?直接赋予null值会报错:Error:(28, 23) Kotlin: Null can not be a value of a non-null type String
println(age) // null
// !! -> 抛出空指针异常(!! -> 表示确实不为空,为空的话自己承担后果,会抛出空指针异常)
val ages = age!!.toInt() //Exception in thread "main" kotlin.KotlinNullPointerException
// age 为空 返回 -1
val agess= age?.toInt() ?: -1 // 这里的 ?. 表示该对象如果不为空,就执行里面的方法
println(agess) // 这个地方 age?.toInt() 因为 age为空 所以不执行 Int 对象中的方法,根据 ?: 运算符->为空返回:后面的值
// ? 在变量后 -> 不做处理返回 null
val agesss = age?.toInt()
println(agesss) // null
具有表示范围的语法(用“..”表示范围,step 表示步长)
for(i in 1..10 step 2){ ... }
i 在 1 到 10 闭区间之内,每次循环一圈,i 根据 step 步长为 2 变化直到超出规定定范围跳出循环
所有类型都是对象,没有 java 中的 int、long、char等基本数据类型,只有装好箱的 Int、Long、Char等包装类型
val by:Byte; // 占用 1 个字节
val sh:Short; // 占用 2 个字节
val c:Int; // 占用 4 个字节
var l:Long; // 占用 8 个字节
// Error: Unresolved reference: int
val ss:int;
增加了 === 操作符,用于确定两个变量是不是引用同一个对象,== 判断两个对象的值是否相等,相当于调用了 equals()
val a:Int = 1000
val b:Int = 1000
println(a == b) // 值相等,返回true
println(a === b) // 对象地址相等,返回true
val boxA:Int? = a
val boxB:Int? = b
// 经过装箱,创建了两个不同的对象,对象地址不同,则返回 false
println(boxA === boxB) // false
println(boxA === a) // false
println(boxB == b) // true
创建对象时不再用 new ,而是直接调用构造方法
// 创建一个对象
val person = Person()
// 对象属性赋值
person.name = "Jackson"
person.sex = "女"
person.age = 20
person.habby = "sing jump rap and basketball"
if 语句可以有返回值
- 作为返回值给变量赋值
val max = if (a > b ) {
println("a is bigger than b")
a
}else {
println("b is bigger than a")
b
}
- 作为函数的返回值
fun smallerNum(num1:Int,num2:Int): Int{
return if (num1 < num2)
num1
else
num2
}
// 简化
fun smallerNum(num1:Int,num2:Int)=if (num1 < num2) num1 else num2
用 when 代替 switch…case,不仅可以判断具体情况,还能判断目标变量值是否在一个范围内
// when 表达式相当于C、java中的switch语句
fun description(month:Int):Unit {
when (month) {
3,4,5 -> println("春天到了,夏天还会远吗?")
6,7,8 -> println("夏天到了,秋天还会远吗?")
9,10,11 -> println("秋天到了,冬天还会远吗?")
12,1,2 -> println("冬天到了,秋天还会远吗?")
else -> println("没有${month}月,输入错误!") // 这里的 else 等同于 default
}
}
- 判断一个值在(
in)或者不在(!in)一个区间或者集合中
fun description(month:Int) {
when (month) {
in 3..5 -> println("${month}月,是春天!")
in 6..8 -> println("${month}月,是夏天!")
in 9..11 -> println("${month}月,是秋天!")
12,1,2 -> println("${month}月,是冬天!")
!in 1..12 -> println("没有${month}月,输入错误!")
}
}
在类中定义的成员变量其实是属性,而不是字段
class Message {
// 标题
var title:String ?= null
// 内容
var content:String ?= null
// 时间戳
var timestamp:Long = 0
}
此类中有三个属性,它们有着对应的 getter 和 setter,但是不能在属性的 getter 或者 setter 代码中使用到属性本身,这样会引起无限递归调用
要解决这个问题,就必须要知道每个属性都有一个不可见的字段存储属性的值,这个字段可以用 field 进行访问:
class Message {
var title: String = "通知"
get() = field + ":" // 将变量 title 赋值并在 getter 时在字符串末尾添冒号
var content: String ?= "从明天起,开始春节放假。"
set(value) {
field = value
}
var timestamp: Long = 0
}
类型转换使用 as 关键字
val a:Any = "abc"
// 如若不使用 as 进行类型转换,a.length 报错
a as String
println(a.length)
- 问题:
首先,声明变量 a 为 Any 数据类型,但是 kotlin 在编译的时候会认为 a 引用的是 Any 类型对象,所以就不能用 a.length
- 解决:
需要使用 as,将 a 引用的类型对象转换成 String 类型,就可以调用 length 方法了
当连续调用某个对象的多个方法时,可以使用 with 关键字,让对象只出现一次
没学 with 关键字用法前:
// langs is a list
val builder = StringBuilder()
builder.append("开始我的编程之路啦!").append("\n")
langs.forEach {
builder.append("我学了").append(it)
builder.append("\n")
}
builder.append("我学完了,头发也掉光了!")
println(builder.toString())
精通后:
// langs is a list
val result = with(StringBuilder()) {
append("开始我的编程之路啦!").append("\n")
langs.forEachIndexed{ index,item ->
append("我学了").append(item).append("\n")
if(index == fruits.lastIndex) {
append("我学完了,头发也掉光了!")
}
}
toString()
}
println(result)
运行结果都是:
但是 Kotlin 提供的 with、apply 语法糖就很舒服,在项目中运用得当能省不少代码呢!
函数的形参列表中个数不定时,可以使用 vararg 定义,在函数内以数组对待
// 可变长参数函数,使用 vararg 关键字声明
fun varArgs(vararg i : Int){
for (vi in i){
print(vi)
}
print('\n')
}
新式语法糖总结
-
能偷懒尽量偷懒,提高开发效率
-
减少人为错误,比如力所能及地支持自动类型推导
-
在语法层面减少空指针异常,这本来是调试中才能发现的错误,但通过把同一类型可为空和不可为空作为两种不同的类型,在编译的时候就可以发现更多逻辑上的错误
-
支持函数式编程
-
可以扩展已存的类的功能,而不必从它派生
结尾
撰文不易,欢迎大家点赞、评论,你的关注、点赞是我坚持的不懈动力,感谢大家能够看到这里!Peace & Love。