Kotlin入坑之路--从排斥到拥抱(上篇)

2 篇文章 0 订阅
1 篇文章 0 订阅

各位吃瓜群众,不要吃瓜了,赶紧入坑呀,早用早享受!!!!!!

  • 为什么要使用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应用。原因以下:

  1. 很多很牛的第三方库已经开始使用kotlin。 如果你不懂kotlin语法,你如何使用这些kotlin库?自己重新写一个Java库吗?
  2. Kotlin代码十分简洁,可以加快开发速度。 语法简洁的好处:同样的功能,kotlin代码量往往都比Java少很多。这样,我们维护其他人的kotlin项目时、查bug时,可以快速阅读代码。
  3. Kotlin代码可以调用java代码,java也可以调用kotlin, 两者可共存。也就是说,学习kotlin代码,你还是可以使用Java代码的。
  4. kotlin提供很多有用的库和方法 使用kotlin,可以加快我们的开发速度。
  5. 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点:
  1. Kotlin没有import static语法
  2. Kotlin可使用 as 关键词更改导入的类名称,避免类名冲突
import com.hh.Student
import com.hbx.test.Student as TestStudent  //可以直接使用TestStudent声明对象了
  1. Kotlin在jvm平台默认导入以下包
kotlin.*
kotlin.annotation.*
kotlin.collections.*
kotlin.comparison.*
kotlin.io.*
kotlin.ranges.*
kotlin.sequences.*
kotlin.text.*
java.lang.*
kotlin.jvm.*
2.2 变量声明

kotlin使用varval声明变量, 其中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: whenswitch强大得多,它允许使用任何对象

举个栗子:

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实战》

五、后续推出 3月8号 推出

4、kotlin类、对象和接口

4.1 接口
4.2 open、final和abstract
4.3 内部类和嵌套类
4.4 类的构造方法和初始化
4.5 类继承
4.6 数据类
4.7 object关键字

5、kotlin lambada

6、kotlin可控性

7、kotlin泛型

8、kotlin注解和反射

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值