一.kotlin相比于java:
java:
1.语法表现力差,例如:java1.8之前不支持lambda表达式,匿名内部的写法繁琐,在函数方面,不支持高洁函数,不支持参数默认值,也不支持函数命名参数。
2.可读性差,难维护 例如:java中的Callback嵌套多层的话,代码可读性就特别差,对维护更加困难
3.并发编程:在java 中,线程的并发以及它复杂的同步机制导致学习成本高。
总结:前期开发效率低,中期线上问题多,后期代码难维护
kotlin:
1.kotlin具有更加简洁的语法跟现代化语法特性,大大提高开发效率,线上奔溃率降低,java中的空指针问题也几乎可以避免
2.兼容性强:kotlin可以跟java混编,大部分旧的项目都是java开发,kotlin强大的兼容性可以让我们渐进的从java迁移到kotlin,不用一次性重写很多代码,从而产生新的问题
3.谷歌宣布kotlin成为官方语言后,现在各大招聘android,都会写着熟悉kotlin的优先,所以koltin我觉得是有非常好的前景的,赶紧入坑。
变量
在java中,如果我们要声明变量,我们必须声明它的类型,后面跟着变量的名称和对应的值,然后以分号结尾。就像这样:
int price = 100;
String msg = "我是msg";
而kotlin则不一样,我们要使用 val 或者是 var这样的关键字作为开头,后面跟“变量名称 -> price ”,接着是变量类型 ->Int 和赋值语句 ,
如下:
var price : Int = 100 ;
在kotlin 中,一般会把末尾的分号省略掉
var price : Int = 100
还有一点我们要注意,在kotlin中,我们应该尽可能的避免使用var ,尽可能多的去使用val。
var 跟 val 的区别
var price = 100 price = 101 //编译通过
val price = 0 price =101 //编译器报错
原因很简单,val声明的变量,我们叫不可变变量,它的值在初始化后就不能再次被修改,它相当于java中的 final 变量。
var声明的变量,我们叫可变变量,它对应java中的普通变量
基础类型
基础类型:包括我们常见的数字类型,布尔类型,字符类型。以及这些类型组成的数组
一切皆对象
在java中,基础类型会分为原始类型和包装类型。比如整型会有对应的int 和 Integer,前者是原始类型,后者是包装类型
int i = 0 ;//原始类型
Integer i = 1 ;//包装类型
java之所以要这么做,是因为原始类型的开销小,性能高,但它不是对象,无法很好的融入到面向对象的系统中。而包装类型的开销大,性能相对较差,但它是对象,可以很好的发挥面向对象的特性。
然而在kotlin语言体系中,是没有原始类型这个概念的,这就意味着,在kotlin里,一切都是对象
实际上,从某种程度上讲,Java 的类型系统并不是完全面向对象的,因为它存在原始类型,而原始类型并不属于对象。而 Kotlin 则不一样,它从语言设计的层面上就规避了这个问题,类型系统则是完全面向对象的。
Val i :Double = 1.toDouble()
可以发现,由于在kotlin中,整型数字“1”被看作对象了,所以我们可以调用它的成员方法toDoubel(),而这样的代码在java是无法实现的。
空安全
既然kotlin 中的一切都是对象,那么对象就有可能为空。也许你会想到写出这样的代码:
val i :Double = null //编译器报错
可事实上,以上的代码是不能通过编译。这是因为kotlin强制开发者在定义变量的时候,指定这个变量是否可能为null 。对于可能为null的变量,我们需要在声明的时候在变量类型后面加一个问号“?”:
val i :Double = null //编译器报错
val j :Double? = null //编译通过
并且kotlin 对可能为空的变量类型做了强制区分,这就意味着,“可能为空的变量”无法赋值给“不可能为空的变量”,当然,反向赋值是没有问题的
var i :Double = 1.0
var j :Double = null
i = j //编译器报错
j = i //编译通过
不过如果你有这种需求的话,也是可以做的:
var i :Double = 1.0
var j : Double = null
If( j != null ) i = j // 编译通过
数字类型
首先,在数字类型上,kotlin 跟 java 几乎是一样的,包括它们对数字“字面量” 的定义方式。
val int = 1 // 整型默认会被推导成“Int” 类型
val long = 12345L // Long类型,我们则需要使用 “L” 后缀
val double = 13.14 // 小数默认会被推导为 “Double ” ,我们不需要使用“D”后缀
val float = 13.14F // Float 类型, 我们需要使用 “F” 后缀
val hexadecimal = OxAF。//使用“Ox” 来代表十六进制字面量
val binary = Ob01010101 //使用“Ob”,来代表二进制字面量
数字类型的转换在 java 是可以隐式转换数字类型,而kotlin更推荐显示转换
例如 int 类型赋值给long类型,编译器会自动为我们做类型转换
int j =100;
long j = i ;
按照java的思维来看,好像是没有问题的,但是你要注意,虽然java编译器不会报错,可它可能会带来问题,因为它们本质不是同一个类型,int long float double 这些类型之间的互相转换是会存在精度问题的,尤其这样的代码掺杂在复杂的逻辑中,在碰到一些边界条件下,即使出现了bug 也很难排查出来
所以在kotlin中 在像这样的隐式转换是被抛弃了。 正确的做法是使用显式调用Int类型的toLong()函数
val i = 100
val j :Long = i.toLong( )
还有比如 toByte()、toShort()、toInt()、toLong()、toFloat()、toDouble()、toChar() 等等。Kotlin 这样设计的优势也是显而易见的,我们代码的可读性更强了,将来也更容易维护了
布尔类型
kotlin 布尔类型的变量 只有两种值 true 和false 布尔类型还支持一些逻辑操作,比如:
“&”代表“与运算”;“|”代表“或运算”;“!”代表“非运算”;“&&”和“||”分别代表它们对应的“短路逻辑运算”。
val i = 1
val j = 2
val k = 3
val isTrue: Boolean = i < j && j < k
字符:Cher
Char 用于表达单个字符 比如'A'. 'B'字符应该用单引号括起来
字符串 :String
字符串用于表达一连串的字符,和java一样,kotlin的字符串也是不可变的,一般用双引号
kotlin还提供非常简洁的字符串模板:
val name = "kotlin"
print("Hello $name!")
在java中我们需要使用两个“+”进行拼接
print("Hello" + name + "!" )
这样看是不是简洁了许些,在字符串格式更复杂的情况下,更能体现出这个特性的简洁
如果我们需要在字符串当中引用更复杂的变量,则需要使用花括号将变量括起来:
val array = array("java","kotlin")
print("Hello ${ array.get( 1 ) } ! " )
Kotlin 还新增了一个原始字符串 ,用三个引号表示:"""
它可以用来存放复杂的多行文本,并且它定义的时候是什么格式,打印也是对应的格式,不需要跟java写一堆的加号和换行符。
val s = """
当我们的字符串有复杂的格式时
原始字符串非常的方便
因为它可以做到所见即所得。 """print(s)
数组
在kotlin中,我们一般会使用 arrayOf()来创建数组
val arrayInt = arrayOf(1,2,3)
val arrayString = arrayOf("apple","pear")
同时kotlin编译器也会根据传入的参数进行类型推导,比如针对这里的arrayInt,由于我们赋值的时候传入的是整数,所以它的类型会被推导为整形数组。
在java中,获取数组的长度,java中应该使用"array.length", 如果是获取List的大小则是使用"list.size".这主要是因为数组不属于java集合。
不过在kotlin中,虽然数组仍然不属于集合,但它的一些操作是跟集合统一的。
val arrayString = arrayOf("apple","pear")
println("Size is ${array.size}")
println("First element is ${array[0]}")
//输出
Size is 2
First element is apple
如上面的代码,我们直接使用array.size就能拿到数组的长度
函数声明
我们看一下以下的代码:
/*
关键字 函数名 参数类型 返回值类型
↓ ↓ ↓ ↓ */
fun helloFunction(name: String): String {
return "Hello $name !"
}/* ↑
花括号内为:函数体
*/
在这段代码中:
- 使用了fun关键字来定义函数
- 函数名称。使用的是驼峰命名法
- 函数参数是以参数名在前,参数类型在后的写法
- 返回值类型,紧跟参数后面
在上面的代码,你会发现它的函数体只有一行代码,针对这种其实我们可以省略函数体的花括号,直接使用 ‘ = ’ 来连接,如下
fun helloFunction(name:String):String = "Hello $name !"
这种写法,我们称为单一表达式函数。
另外,由于kotlin支持类型推导,我们可以把返回值的类型也省略掉:
fun helloFunction(name:String) = "Hello $name !"
函数调用
如果我们想调用它:
heeloFunction("kotlin")
不过,kotlin还提供一些新的特性,那就是命名参数,简单来说,就是它允许我们在调用函数时传入“形参的名字”。
helloFunction(name = "kotlin")
让我们看一下更具体的使用场景:
fun createUser( name: String,
age: Int,
gender: Int,
friendCount: Int,
feedCount: Int,
likeCount: Long,
commentCount: Int) {
//.....省略
}
这是一个包含很多参数的函数,在kotlin中,我们一般提倡以纵向的方式排列,这样更符合我们的阅读习惯
但是,如果我们像java那样调用createUser ,代码会非常难以阅读:
createUser("Tom ",30,1,78,2093,10937,1211)
这里的第一个参数,我们知道肯定是name ,但是到了后面的一堆数字,就容易迷惑。
createUser(
name = "Tom",
age = 30,
gender = 1,
friendCount = 78,
feedCount = 2093,
likeCount = 10937,
commentCount = 3285
)
如果我们这样写的话,可读性是不是变得更加强了呢。这里体现出kotlin命名参数的可读性跟易维护性两个优势
除了命名参数这个特性,kotlin还支持参数默认值。
fun createUser(
name: String,
age: Int,
gender: Int = 1,
friendCount: Int = 0,
feedCount: Int = 0,
likeCount: Long = 0L,
commentCount: Int = 0
) {
//..
}
我们可以看到,里面的 参数大部分都被赋予了默认值,这样的好处在于,我们在调用时候就可以省很多事情,比如,下面这段代码只需要传3个参数,剩下的4个参数不传,编译器就会自动帮我们填上默认值。
createUser(
name = "Tom",
age = 30,
commentCount = 3285
)
流程控制
在kotlin中,流程控制主要有 if ,when,for ,while
if
if主要用于逻辑判断。跟java基本一致
val i = 1
if (i > 0) {
print("Big")
} else {
print("Small")
}输出结果:
Big
我们也可以这样写:
val message = if (i > 0) "Big" else "Small"
print(message)
另外,由于kotlin明确规定类型分为 “可空类型” 跟“不可空类型”,因此,我们会经常遇到可空的变量,并且要判断它们是否为空:
fun getLength(text: String?): Int {
return if (text != null) text.length else 0
}
在这个例子中,我们把if当成表达式,如果text不为空,就返回它的长度,如果为空,则返回0
在kotlin中,类型这样的逻辑判断出现的很频繁,因此,kotlin针对这种情况提供一种简写,叫做Elvis表达式。
fun getLength(text: String?): Int {
return text?.length ?: 0
}
when
when语句,一般我们的逻辑只有两个分支时候,我们直接用if/else,而大于两个逻辑分支,我们使用when
val i :Int =1
when( i ){
1 -> print("1")
2 -> print ("2")
else -> print("i 不是1也不是2")
}
when语句有点像java里面的 switch case语句,不过kotlin的when更加强大,它也可以作为表达式,为变量赋值:
val i :Int =1
val message = when( i ){
1 -> "一"
2 -> "二"
else -> print("i 不是1也不是2")
}
println(message)
循环迭代:while 与 for
首先while循环,它在使用上跟java没有什么区别,都是用于重复执行某些代码
var i = 0
while (i <= 2) {
println(i)
i++
}var j = 0
do {
println(j)
j++
} while (j <= 2)输出结果:
0
1
2
0
1
2
for
但是对于for语句,kotlin跟java的用法就不一样了。
kotlin的for语句更多用于“迭代” 如下代码就代表迭代array这个数组里的所有元素,程序会依次打印出 “1,2,3”。
val array = arrayOf(1, 2, 3)
for (i in array) {
println(i)
}
除了迭代数组跟集合外,kotlin还支持迭代一个区间。
首先,我们要定义一个区间,可以使用“..”来连接数组的区间的两端,比如“1..3”就代表从1到3的封闭间,左闭右闭
val oneToThree = 1..3 //代表[1,3]
接着,我们可以使用for语句,对这个闭区间范围进行迭代:
for (i in oneToThree) {
println(i)
}输出结果:
1
2
3
甚至,我们可以逆序迭代一个区间,比如:
for (i in 6 downTo 0 step 2) {
println(i)
}输出结果:
6
4
2
0
以上代码的含义就是逆序迭代一个区间,从6到0,每次的迭代的步长是2。需要注意的是,逆序区间我们不能使用“6..0”来定义。而是使用downTo