《第一行代码》Kotlin快速入门(自用存档)

《第一行代码》

第2章

2.1 Kotlin语言介绍

2.2 如果运行Kotlin

2.3 变成之本:变量和函数

2.3.1 变量
  • val(value的简写):不可变的变量。初始赋值后再也不能重新复制,对应Java中的fianl变量
  • var(variable的简写):可变的变量。初始复制之后仍然可以在被重新赋值,对应Java中的非final变量
  • 变量名:变量类型:显示地声明变量类型
2.3.2 函数

函数和方法都是同一个概念。函数:function,方法:method 没有区别

Kotlin中函数的叫法更普遍

fun 方法名(参数): 返回值类型{
    return 返回值
}

fun largerNumber(num1: Int, num2: Int): Int{
    return max(num1, num2)
}

// 语法糖
// 当函数中只有一行代码:函数体可省略
// 中间用等号(=)连接
// return 关键字也可省略,等于号足以表达返回值的意思
fun largerNumber(num1: Int, num2: Int): Int = max(num1, num2)

// Kotlin出色的推导机制,max函数返回的是一个Int值
// largerNumber()函数的尾部又使用等号连接max()函数
// Kotlin可以推导出largerNumber()函数的返回值必然是一个Int值
fun largerNumber(num1: Int, num2: Int) = max(num1, num2)

2.4 程序的逻辑控制

2.4.1 if条件语句
// 和Java几乎一样
fun largerNumber(num1: Int, num2: Int): Int {
    var value = 0
    if (num1 > num2){
        value = num1
    } else {
        value = num2
    }
}

// if有返回值
// 每个条件的最后一行代码作为返回值
fun largerNumber(num1: Int, num2: Int): Int {
    val value = if (num1 > num2) {
        num1
    } else {
        num2
    }
    return value
}

fun largerNumber(num1: Int, num2: Int) = if (num1 > num2) num1 else num2
2.4.2 when条件语句
// 如果用if
fun getScore(name: String) = if (name == "Tom") {
    86
} else if (name == "Jim") {
    77
} else if (name == "Jack") {
    95
} else if (name == "Lily") {
    100
} else {
    0
}

// when语句允许传入任意类型的参数,然后可以在when的结构体中定义一系列的条件
// 匹配值 -> { 执行逻辑 }
fun getScore(name: String) = when (name) {
    "Tom" ->87
    "Jim" ->77
    "Jack" -> 95
    "Lily" -> 100
    else -> 0
}

// 当执行逻辑只有一行时{ }可以省略
// when还允许类型匹配
// is相当于java中的instanceof关键字
// Number类型时Kotlin内置的一个抽象类,Int、Long、Float、Double等与数字相关的类都是它的子类
fun checkNumber(num: Number){
    when (num){
        is Int -> println("number is Int")
        is Double -> println("number is Double")
        else -> println("number not support")
    }
}

// 不带参数用法
// Kotlin中判断字符串或对象是否相等可以直接使用==关键字
fun getScore(name: String) = when {
    name == "Tom" ->87
    name == "Jim" ->77
    name == "Jack" -> 95
    name == "Lily" -> 100
    else -> 0
}

// 有些情况必须使用不带参数的when
fun getScore(name: String) = when {
    name.startsWith("Tom") ->87
    name.startsWith("Jim") ->77
    name.startsWith("Jack") -> 95
    name.startsWith("Lily") -> 100
    else -> 0
}
2.4.3 循环语句

和Java一样提供了while循环和for循环,其中while循环不管使用语法还是使用技巧上都和Java中的while循环没有任何区别,因此直接跳过while循环

// 创建0-10的区间,并且两端都是闭区间,一位置0到10这两端点都是包含在区间中的
// [0,10]
// .. 创建两端闭区间的关键字
val rnge = 0..10

// for-in循环遍历
for main(){
    for(i in 0..10){
        println(i)
    }
}

// 左闭右开区间
// 0到10的左闭右开区间
// [0,10)
val range = 0 until 10

// for-in默认每次循环执行时会在区间范围内递增1
// step跳过其中一些元素
fun main(){
    for(i in 0 until 10 step 2){
        println(i)
    }
}

// ..和until关键字都要求区间的左端必须小于等于区间的右端
// 也就是说这两种关键字的创建都是一个升序的区间
// 创建一个降序区间 downTo
// [10,1]的降序区间
// 也可使用 step关键字
fun main(){
    for(i in 10 downTo 1){
        prinln(i)
    }
}

2.5 面向对象编程

2.5.1 类与对象
class Person{
    var name = ""
    var age = 0
    
    fun eat(){
        println(name + "is eating.He is" + age + " year.old.")
    }
}

// 实例化Person类,省区了Java中的new关键字
val p = Person()
2.5.2 继承与构造函数
// 声明Person类可被继承
open class Person{
    ...
}

class Student : Person(){
    var sno = ""
    var grade = 0
}

// 主构造函数
// 每个类默认都会有一个不带参数的主构造函数
// 特点是没有函数体,直接定义在类名后面即可
// 由于构造函数的参数是在创建实例的时候传入的,不像之前的写法那样还得重新赋值,因此我们可以将参数全部声明成val
// 子类实例化时会调用父类构造
// 子类的主构造函数调用父类中的哪个构造函数,在继承的时候通过括号来指定
// 48行问题:也就是说这个继承类后面的括号是服务于主构造函数的,当没有显示声明主构造函数,并写了次构造时,继承类后面的括号作用也就失效了。
class Student(val sno: String, val grade: Int) : Person(){
    init {
        println("sno is " + sno)
        println("grade is " + grade)
    }
}
var student = Student("a123",5)

// 继承
open class Person(val name: String, val age: Int){
    ...
}
// 在主构造函数中添加name和age字段时,不能再将他们声明成val或var,因为var或val会将参数自动成为该类的字段,会导致和父类中同名的name和age字段造成冲突。
// 因此,这里的name和age参数前面不用加任何关键字,让他作用域仅限定在主构造函数当中
class Student(val sno: String, val grade: Int, name: String, age: Int) : Person(name, age){
    
}

// 次构造函数
class Student(val sno: String, val grade: Int, name: String, age: Int) : Person(name, age){
    // this关键字调用主构造函数
    constructor(name: String, age: Int) : this("", 0, name, age){
        
    }
    // this关键字调用次构造函数
    constructor() : this("", 0){
        
    }
}

class Student : Person{
    // 没有现实定义主构造函数,同时又因为定义了次构造函数
    // !!!此时Student类是没有主构造函数的
    // 此时继承Person类时页就不需要再加上括号了
    // 次构造函数只能直接调用父类的构造函数 super关键字!
    constructor(name: String, age: Int) : super(name, age){
        
    }
}
2.5.3 接口

与Java几乎完全一致。单继承多实现

insterface Study{
    fun readBooks()
    fun doHomework()
}

// Kotlin中继承与实现统一使用冒号,中间用逗号进行分隔
// 接口后面不用加括号,因为接口没有构造函数
class Student(name: String, age: Int) : Person(name, age),Study{
    override fun readBooks(){
        ...
    }
    
    override fun doHomework(){
        ...
    }
}

fun main(){
    val student = Student("Jack", 19)
    doStudy(student)
}

fun doStudy(study: Study){
    study.readBooks()
    study.doHomework
}

// Kotlin允许对接口中定义的函数进行默认实现(Java1.8也开始支持)
// 如果有默认实现,就不强制子类实现
insterface Study{
    fun readBooks()
    fun doHomework(){
        ...
    }
}
修饰符JavaKotlin
public所有类可见所有类可见(默认)
private当前类可见当前类可见
protected当前类、子类、同一包路径下的类可见当前类、子类可见
default同一包路径下的类可见(默认)
internal同一模块中的类可见
2.5.4 数据类与单例类
// data关键字
// Kotlin会根据主构造函数中的参数将equals() hashCode() toString() 等固定且无实际逻辑意义的方法自动生成
data class Cellphone(val brand: String, val price: Double)

// object关键字
// 单例类
object Singleton{
    fun singletonTest(){
        ...
    }
}

// 单例类调用
Singleton.singletonTest()

2.6 Lambda 编程

2.6.1 集合的创建及遍历
val list = ArrayList<String>()
list.add("Apple")
list.add("Banana")
...

// Kotlin内置listOf()函数来简化初始化集合
// listOf()函数创建的时不可变的集合
val list = listOf("Apple","Banana")
for(fruit in list){
    println(fruit)
}

// mutableListOf()函数创建可变集合
val list = mutableListOf("Apple","Banana")
list.add("Watermelon")
for...

// setOf()和mutableSetOf()和List集合用法一样

// 与Java相似创建map
val map = HashMap<String, Int>()
map.put("Apple", 1)
...
// Kotlin并不建议使用put()和get()方法来对Map进行添加和读取数据操作
// 添加数据
map["Apple"] = 1
// 读取数据
val number = map["Apple"]
// Kotlin也提供了一对 mapOf()和mutableMapOf()函数来简化Map用法
// to并不是关键字,而是一个infix函数
val map = mapOf("Apple" to 1, "Banana" to 2)

// 遍历map集合
for((fruit, number) in map){
    println("fruit is " + fruit + ", number is " + number)
}

2.6.2 集合的函数时API
// Lambda就是一小段可以作为参数传递的代码
// Kotlin对Lambda没有进行限制,但时通常不建议再Lambda表达式中编写太长的代码,否则可能会影响代码的可读性
// Lambda表达式的语法结构:
// {参数名1: 参数类型, 参数名2: 参数类型 -> 函数体}
// 如果有参数传入到Lambda表达式中的话,需要声明参数列表,参数列表结尾使用->符号,表示参数列表结束以及函数体的开始,函数体中可以编写任意行代码(虽然不建议编写太长的代码),并且最后一行代码会自动作为Lambda表达式的返回值
val list = listOf("Apple", "Banana", "Orange", "Pear")
var lambda = { fruit: String -> fruit.length }
val maxlengthFruit = list.maxBy(lambda)
// 直接将lambda表达式传入maxBy
val maxlengthFruit = list.maxBy({ fruit: String -> fruit.length })
// Kotlin规定,当Lambda参数是函数的最后一个参数时,可以将Lambda表达式移到函数括号的外面
val maxlengthFruit = list.maxBy(){ fruit: String -> fruit.length }
// Lambda参数时唯一一个参数的话,可以将函数的括号省略
val maxlengthFruit = list.maxBy{ fruit: String -> fruit.length }
// Lambda表达式中的参数列表大多数情况下不必声明参数类型
val maxlengthFruit = list.maxBy{ fruit -> fruit.length }
// Lambda表达式的参数列表中只有一个参数时,也不必声明参数名,而是可以使用it关键词来代替
val maxlengthFruit = list.maxBy{ it.length }

集合中比较常用的函数式API

// map:将集合中的每个元素都映射成一个另外的值,映射的规则再Lambda表达式中指定,最终生成新的集合
fun main(){
    val list = listOf("Apple", "Banana", "Orange", "Pear")
    // 转换成大写
    val newList = list.map{ it.toUpperCase() }
    for (fruit in newList){
        println(fruit)
    }
}
// filter:过滤集合中的数据
fun main(){
    val list = listOf("Apple", "Banana", "Orange", "Pear")
    // 先过滤字母长度小于5的,再转换成大写
    val newList = list.filter{ it.length <= 5 }.map { it.UpperCase()}
    for (fruit in newList){
        println(fruit)
    }
}
// any:判断集合中是否至少存在一个元素满足指定条件
// all:判断集合中是否所有元素都满足指定条件
fun main(){
    val list = listOf("Apple", "Banana", "Orange", "Pear")
    val anyResult = list.any{ it.length <=5 }
    val allResult = list.all{ it.length <=5 }
}
2.6.3 Java函数式API的使用

在Kotlin代码中调用了一个Java方法,并且该方法接收一个Java单抽象方法接口参数,就可以使用函数式API

单抽象方法接口:接口中只有一个抽象方法,如果有多个,则无法使用函数式API

// Java:
new Thread(new Runnable(){
    @Override
    public void run(){
        System.out.println("Thread is running")
    }
})
// Kotlin:
// Kotlin完全舍弃了new关键字,因此创建匿名类实例的时候不能再使用new了,而是改成了object关键字
// 然而并没有比Java简化到哪里去
// Thread类的构造方法时符合Java函数式API的使用条件的
Thread(Object : Runnable {
    override fun run(){
        println("Thread is running")
    }
})
// 简化:Runnable是单抽象方法接口
Thread(Runnable {
    println("Thread is running")
}).start()
// 简化:Thread只有一个参数
Thread({
    println("Thread is running")
}).start()
// 简化:Lambda表达式是方法的最后一个参数时
Thread{
    println("Thread is running")
}.start()

2.7 空指针检查

Java判空方式

if(study != null){
    study.readBooks();
    study.doHomework();
}
2.7.1 可空类型系统

Kotlin默认所有的参数和变量都不可为空

Kotlin的判空方式

    • 放置在类名后面表示可为空类型:需要处理空指针异常

      fun doStudy(study: Study?){
          if(study != null){
              study.readBooks()
              study.doHomework()
          }
      }
      
2.7.2 判空辅助工具

Kotlin提供了一系列的辅助工具,使开发者能够更轻松的进行判空处理。

  • ?.

    • 当对象不为空时正常调用相应方法,当对象为空时则什么都不做

      study?.readBooks();
      study?.doHomework();
      
  • ?:

    • 左右两边都接收一个表达式,如果左边表达式的结果不为空就返回左边表达式的结果,否则就返回右边表达式的结果。

      // text不为空时返回length,否则返回0
      fun getTextlength(text: String)text?.length ?: 0
      
  • !!

    • 当我们可能从逻辑上已经将空指针异常处理了,但Kotlin的编译器并不知道,还是会编译出错
      // kotlin忘了叫啥,java叫属性、字段、域
      var content: String?= "hello"
      
      // main方法,调用printUpperCase
      fun main(){
          if(content != null){
              printUperCase()
          }
      }
      
      // 编译不通过!!!
      // 无法运行,方法内不知道在main方法中已经对content变量进行非空检查
      // content转换成大写并打印
      fun printUpperCase(){
          val upperCase = content.toUpperCase()
          println(upperCase)
      }
      
      // 使用“!!”
      // 告诉Kotlin,我非常确信这里的对象不会为空
      fun printUpperCase(){
          val upperCase = content!!.toUpperCase()
          println(upperCase)
      }
      
  • let

    • 函数(方法),提供了函数式API的编程接口并将原始调用对象作为参数传递到Lambda表达式

      // obj=obj2
      obj.let{ obj2 ->
          // 编写具体的业务逻辑
      }
      
    • let特性配合**?.**操作符可以在空指针检查时起到很大作用

      fun doStudy(study: Study?){
          study?.let{ stu ->
              stu.readBooks()
              stu.doHomework
          }
      }
      
      // 优化:当Lambda表达式的参数列表中只有一个参数时,可以不用声明参数名,直接使用it关键字来代替即可
      fun doStudy(study: Study?){
          study?.let{
              it.readBooks()
              it.doHomework()
          }
      }
      
    • 处理全局变量的判空

      var study: Study? = null
      
      // 报错!
      // 全局变量的值随时都有可能被其他线程锁修改(比如赋值为null),即使做了判空处理,也仍然无法保证if语句中的study变量没有空指针风险。
      // 从这点上也能体现出let函数的优势
      fun doStudy(){
          if(stu != null){
              study.readBooks()
              study.doHomework()
          }
      }
      

2.8 Kotlin中的小魔术

2.8.1 字符串内嵌表达式
  • ${}

    // 表达式执行结果替代这部分内容
    "hello,${obj.name}. nice to meet you!"
    // 当表达式中仅有一个变量时,省略大括号
    "hello,${obj.name}. nice to meet you!"
    
2.8.2 函数的参数默认值
fun printParams(num: Int, str: String = "hello"){
    println("num is $num,str is $str")
}
printParams(100)

// 当第一个参数有默认值
fun printParams(num: Int = 100, str: String){
    println("num is $num,str is $str")
}
printParams(str = "word")
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值