各位吃瓜群众,不要吃瓜了,赶紧入坑呀,早用早享受!!!!!!
- 为什么要使用kotlin
- kotlin语法
- 学习kotlin心得体会
- 相关学习资料推荐
- 后续推出
一、为什么要使用kotlin
1、入坑前:排斥
第一次接触Kotlin的Android项目时,我表示很排斥,I hate it !!! 看到陌生的kotlin语法,我心里十分十分抗拒,心里想:"为什么要使用kotlin啊,我不会kotlin啊,使用Java不是挺好的吗,Java是世界上最好的语言..."
于是,机智的我花了3天左右时间, 成功地将其他同事辛辛苦苦写好的Kotlin代码转为Java代码。转换后的Java代码,嗯,看起来,果然赏心悦目,于是,心情大好,当天解了10000个bug。
2、入坑后: 拥抱
看了Google开发者大会,发现Goggle爸爸已经使用kotlin编写Android代码了,也已经将kotlin作为Android开发语言。同时,Github上很多很牛的第三方库都逐渐开始将Java代码转为Kotlin代码。此时再不入坑,再抗拒,再不跟上时代,我就会成为一个古板的落后的开发者,我会被时代淘汰,最后只能回去买个三轮车,摆摊卖手抓饼了(其实卖手抓饼也很赚钱的,收入可能比程序员还高,所以如果你不想打代码了,现在也可以考虑去卖手抓饼了。。。)
现在,我来告诉你,为什么你需要学习和使用kotlin编写Android应用。原因以下:
很多很牛的第三方库已经开始使用kotlin。
如果你不懂kotlin语法,你如何使用这些kotlin库?自己重新写一个Java库吗?Kotlin代码十分简洁,可以加快开发速度。
语法简洁的好处:同样的功能,kotlin代码量往往都比Java少很多。这样,我们维护其他人的kotlin项目时、查bug时,可以快速阅读代码。Kotlin代码可以调用java代码,java也可以调用kotlin, 两者可共存。
也就是说,学习kotlin代码,你还是可以使用Java代码的。kotlin提供很多有用的库和方法
使用kotlin,可以加快我们的开发速度。kotlin除了可以应用于Android, 也可以用于后台和前端开发。
虽然用于后台和前端开发目前还不够成熟,但随着这门语言的逐步完善,极大可能被后台和前端广泛使用。当然,在Android使用kotlin开发已经很成熟,很多大公司都已经采用了kotlin开发,所以我们不必顾虑。
总的来说,学会kotlin,不亏,很赚。
二、kotlin语法
看到这里,你心里肯定要骂我了:我就想学习kotlin语法而已,还要看你一堆废话,马上给我介绍kotlin语法啊,再不介绍,我就要砍你了。。。小伙子,别,别冲动啊,听你的,现在立刻马上开始介绍kotlin语法。
1、什么是kotlin
kotlin是JetBrains在2010年发明的一门语言,目标是替换Java
。 它和Java一样,是一门静态语言
,编写的文件后缀为.kt
。Kotlin语言包括自己的编译器、库和相关工具, 也就是说,编写的kotlin代码,要使用kotlin专有的编译器
。Kotlin是免费并开源的。kotlin代码编译流程如下图:
通过这个流程图,我们可以发现,kotlin代码也会编程成 .class
文件。这就是kotlin能和Java相互调用的原因。这里有一个小小的学习技巧:
我们可以将kotlin编译后的.class文件反编译为Java文件,通过java文件和kotlin文件对比,可以方便我们理解kotlin的某些语言特性。后面会介绍到如何使用这个技巧。
2、kotlin基础
2.1 没有基本类型
kotlin语法上没有基本类型的概念,kotlin对应Java基本类型的对象为Int、Long、Float、Boolean、Char等等。你可能会想,kotlin连基本类型都使用对象,那岂不是很浪费内存和效率?放心,kotlin只是在语法上声明为Int、Boolean等,实际上kotlin的编译器在编译代码时,会根据你的代码实际情况,来决定底层是使用基本类型还是使用对象,所以你不用担心效率和内存问题。
2.2 package和import
2.2.1 package
使用方式和Java一样,声明在文件的最前面
2.2.2 使用import关键字,使用方式和Java一样。与java不同主要3点:
- Kotlin
没有import static
语法 - Kotlin可使用
as
关键词更改导入的类名称,避免类名冲突
import com.hh.Student
import com.hbx.test.Student as TestStudent //可以直接使用TestStudent声明对象了
- Kotlin在jvm平台默认导入以下包
kotlin.*
kotlin.annotation.*
kotlin.collections.*
kotlin.comparison.*
kotlin.io.*
kotlin.ranges.*
kotlin.sequences.*
kotlin.text.*
java.lang.*
kotlin.jvm.*
2.2 变量声明
kotlin使用var
和 val
声明变量, 其中var声明的变量是可变的, val声明的变量是不可变的(你可以认为Java的final)。噢,对了,Kotlin语言不需要(;
)
//声明一个Int变量并给与一个初始值
var age : Int = 26
//同上,也是声明一个Int变量并给与一个初始值。kotlin可以自动推导类型
var age = 26
//当声明类型不赋予初始值时,一定要声明类型
var age : Int
age = 26
val height : Boolean = false
height = true //这行编译不通过的,因为你声明的height是使用val关键字的,所以不可以变了。
2.3 方法声明
- (1) 方法声明使用
fun
关键字,格式如下:
fun 方法名(参数1,参数2) : 返回类型 {
方法体
}
Notic: 当省略返回类型声明时,会返回Unit,相当于java的void
举个栗子:
fun add(a:Int, b:Int) : Int {
return a + b
}
var result = add(1, 2)
- (2) 当函数体只有单个表达式时,方法可以写成以下方式:
fun 方法名(参数1,参数2) : 返回类型 = 方法体
举个栗子:
fun add(a:Int, b:Int) : Int = a + b
//因为kotlin可以类型推导,所以省略返回类型声明,故上述表达式也可以写成
fun add(a:Int, b:Int) = a + b
2.4 类声明
类声明使用class
关键字
- 声明类
// kotlin
class Person(val name: String, var age: Int) {
fun sayHello() {
println("name:$name,age:$age")
}
}
以上代码生成的class后,反编译的java代码如下, 也就是说,kotlin简化了代码。由编译器对var变量会生成set/get方法,对val生成get方法:
//java
public final class Person {
@NotNull
private final String name;
private int age;
public final void sayHello() {
String var1 = "name:" + this.name + ",age:" + this.age;
boolean var2 = false;
System.out.println(var1);
}
@NotNull
public final String getName() {
return this.name;
}
public final int getAge() {
return this.age;
}
public final void setAge(int var1) {
this.age = var1;
}
public Person(@NotNull String name, int age) {
Intrinsics.checkParameterIsNotNull(name, "name");
super();
this.name = name;
this.age = age;
}
}
- 使用类
val person = Person("hh", 26)
person.age = 16 // 相当于 person.setAge(16)
var result = person.age // 相当于 var result = person.getAge()
person.sayHello() //此时会打印 name:hh, age:16
2.5 字符串模板
- 使用字符串模板,可以更简单格式化字符串。kotlin允许你在字符串中引入变量,只需要在变量前加一个
$
号。
举个栗子:
val name = "hh"
val age = 26
prinln("hello, my name is $name, age $age")
- 除了在字符串中使用变量,也可以使用表达式, 使用
{}
, 在{}
中写入表达式
举个栗子:
val person = Person("hh", 26)
prinln("hello, my name is ${person.name}, age ${person.age}")
2.6 if/else
- 在kotlin里面,if/else是表达式,不是语句, 而在java中,if/else是语句。表达式和语句区别:表达式可以有返回值,语句没有返回值。所以在kotlin里面,if/else可以写成这样:
普通程序员可以这样写:
val a = 10
val b = 100
var result = 0
if (a > b){
result = a
} else {
result = b
}
学了kotlin程序员可以这样写:
val a = 10
val b = 100
val result = if (a > b) a else b
- kotlin里面没有java里面的
? :
三目运算符。因为if/else是表达式,可以返回值,可以直接替代? :
三目运算符(题外话:? :
三目运算符这么好用,kotlin竟然不加进来, 我要举报它!!)
2.7 when
在kotlin里面,没有switch语句
!!!使用when
处理选择逻辑,when也是一个表达式,可以返回值。when使用->
进行分支选择,当所有分支都不符合时,会调用else
分支。Notice: when
比switch
强大得多,它允许使用任何对象
举个栗子:
enum class Color{
YELLOW, WHITE, RED
}
fun getColorMean(color: Color) : String {
var name = ""
name = when(color) {
Color.YELLOW -> "黄"
Color.WHITE -> "白"
Color.RED -> "熊"
else -> "No color"
}
return name
}
fun main(array: Array<String>) {
val color = getColorMean(Color.YELLOW)
}
2.8 while循环和for循环
- while语法和java一样,没啥好说的。while是语句,没有返回值
while(condition) {
// do someting
}
do {
// do someting
} while (condition)
- for循环。for是语句,没有返回值。 for使用
in
来迭代集合、区间和序列
//遍历0到10, 即闭区间[0, 10]
for (index in 0..10){
print(index)
}
//遍历0到9, 即开区间【0,10)
for(index in 0 until 10) {
print(index)
}
//遍历0到10, 即开区间【0,10], 每次+2
for(index in 0 .. 10 step 2) {
print(index)
}
//遍历10到0, , 每次-2
for(index in 10 downTo 0 step 2) {
print(index)
}
// 遍历集合元素值
val list = arrayListOf(1, 2, 3, 4)
for (value in list) {
println(value)
}
// 遍历集合元素索引和值
val list2 = arrayListOf(1, 2, 3, 4)
for ((index, value) in list2.withIndex()) {
println("index:$index, value:$value")
}
- 使用
in
或!in
可以检查集合和区间的成员
举个栗子:
val list3 = arrayListOf(1, 2, 3, 4)
if (1 in list3) {
println("1 in list3")
}
if (1 !in list3) {
println("1 not in list3")
} else {
println("1 in list3")
}
2.9 try/catch
和java一样,使用 try/catch/finally捕获和处理异常,使用throw抛出异常。 和java不一样,try/catch/finally是表达式,不是语句,可以返回值。throw也是一个表达式。
- 使用try/catch/finally
val price = "price12"
var value = 0
value = try {
"12".toInt()
} catch(e : Exception) {
0
} finally {
// do nothing
}
- throw关键字使用
val num = 101
val present = if (num in 0..100) {
num
} else throw IllegalArgumentException("num is illegal")
print(present) // 这行不会打印,因为num不在0到100范围内,已经抛出异常
- kotlin不支持使用throws声明方法异常, 方法内可以抛出异常,但不要声明抛出的异常
fun errorTest() {
throw Exception("error")
}
try {
errorTest()
} catch (e : Exception) {
// do nothing
}
2.10 枚举类
使用enum class声明枚举类。
enum class Color {
YELLOW, WHITE, RED
}
2.11 智能转换
is
关键字判断对象是否是某种类型,作用如java的instanceOf。使用as
关键字进行类型强转。当你使用is
判断类型后,在该作用域内,就不需要as
转换类型了。kotlin编译器已经帮你智能转换,可以直接使用,下面给出代码展示:
interface Expr // 声明一个接口
class Num(val value : Int) : Expr //声明一个类,实现了Expr接口
fun test(expr : Expr) {
if (expr is Num) {
println(expr.value) //当你使用is判断类型后,在该作用域内, kotlin编译器已经帮你智能转换,可以直接使用
}
if (expr is Num) {
val temp = expr as Num // 也可以自己转换,但没必要啊。当你使用is判断类型后,在该作用域内,kotlin编译器会帮你智能转换。
println(temp.value)
}
}
3、kotlin方法
3.1 命名参数
命名参数的作用:提高函数可读性。调用函数时,通过指定参数的名称设置值。例子:
fun main(array: Array<String>) {
//完全没有可读性可言,传入这些值很容易出错
test("China", "GuangDong","GuangZhou", "HH")
//通过命名参数,可读性大大提高,不容易出错
test(country = "China", province = "GuangDong", city = "GuangZhou", name = "HH")
}
fun test( country : String, province : String, city : String, name: String ) {
print("$country, $province, $city, $name")
}
3.2 默认参数
默认参数作用,可避免多个方法重载。话不多说,上码:
fun main(array: Array<String>) {
test("China")
test("China", "HuBei", "WuHan")
test("China", "GuangDong","GuangZhou", "HH")
}
fun test( country : String, province : String = "GuangDong", city : String = "GuangZhou", name: String ="HH" ) {
print("$country, $province, $city, $name")
}
3.3 顶层函数和属性
顶层函数和属性可以消除静态类。函数和属性可以直接写在文件里面,不必写在类里面。
//Test.kt 这里是一个kt文件
val topValue = 100
fun topTest() {
println(topValue)
}
class Person(val name: String)
反编译后的java代码如下:
// TestKt.java
public final class TestKt {
private static final int topValue = 100;
public static final int getTopValue() {
return topValue;
}
public static final void topTest() {
int var0 = topValue;
boolean var1 = false;
System.out.println(var0);
}
}
// Person.java
public final class Person {
@NotNull
private final String name;
@NotNull
public final String getName() {
return this.name;
}
public Person(@NotNull String name) {
Intrinsics.checkParameterIsNotNull(name, "name");
super();
this.name = name;
}
}
通过看反编译后的java代码,我想你已经明白了。其实,之所以可以使用顶层函数和顶层属性,是因为编译器会将我们顶层函数和顶层属性编译到一个类中,类名为kt文件,然后将顶层函数和顶层属性作为静态成员。
3.4 扩展函数和属性
通过扩展函数和属性,我们可以在不破坏现有类的结构下,为该类增加方法。
举个栗子,比如我要为String增加一个扩展方法,用于获取字符串的最后一个字符:
//定义扩展方法
//扩展函数
fun String.lastChar() = this.get(this.length - 1)
//扩展属性
val String.last : Char
get() = this.get(this.length - 1)
fun main(array: Array<String>) {
val hbxLastChar = "HBX".lastChar()
println(hbxLastChar) // 打印 X
}
反编译后的java代码如下:
public final class TestKt {
public static final char lastChar(@NotNull String value) {
return value.charAt(value.length() - 1);
}
public static final char getFirst(@NotNull String value) {
return value.charAt(0);
}
public static final void main(@NotNull String[] array) {
char hbxLastChar = lastChar("HBX");
boolean var2 = false;
System.out.println(hbxLastChar);
char var4 = getFirst("HBX");
boolean var3 = false;
System.out.println(var4);
}
}
看到反编译后的java代码,你会发现,所谓的扩展函数和属性,不过是编译器将扩展函数和属性编译为静态函数,"HBX"在调用扩展方法时,其实是将这个"HBX"字符串对象传入静态方法中。所以,当你明白了这些代码后,你应该要知道两点
- 编写扩展函数或属性时,不能调用this对象的私有方法和protected方法。
- 扩展函数不存在重写, 例子如下:
open class Expr()
class Number : Expr()
fun Expr.show() {
print("I am Expr")
}
fun Number.show() {
print("I am Number")
}
fun main(array: Array<String>) {
val num : Expr = Number()
num.show() //这里会打印I am Expr, 因为扩展方法不支持重载,原因请细细品味上面的反编译代码和解说
}
3.5 可变参数
作用,可以任意传递多个参数给该函数。使用vararg
关键字
fun main(array: Array<String>) {
test("H", "H")
test("H", "B", "X")
val stringArray = arrayOf("1","2","3")
test(*stringArray) // * 为展开运算符,可以展开数组内容
val stringList = arrayListOf("1","2")
val listArray = stringList.toTypedArray() // 先将列表转为数组
test(*listArray)
}
fun test(vararg params : String) {
for (value in params ) {
print(value)
}
}
3.6 局部函数
在函数内部,可以定义类和方法。作用:使代码更简洁
class User(val name: String, val address : String)
fun saveUser(user: User) {
//定义局部函数,精简逻辑
fun checkUserFiled() {
if (user.name.isEmpty()) {
throw IllegalArgumentException("name is null")
}
if (user.address.isEmpty()) {
throw IllegalArgumentException("address is null")
}
}
checkUserFiled()
//开始保存数据
}
这里抛出一个问题,在内部可以定义函数和类的原理是什么?实现原理其实很简单的,感兴趣的童鞋可以自己反编译代码瞄瞄。
三、学习kotlin心得体会
- 不要排斥新事物,要与时俱进。
- 刻苦学习几天或几个月,就可以学会一个终身受用的技能。所以,对于技能学习,无论多辛苦,都要坚持。
四、相关学习资料推荐
- 书籍 《kotlin实战》