IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Kotlin笔记 -> 正文阅读

[移动开发]Kotlin笔记

# Kotlin

变量和函数

变量

  • 如果代码要定义一个变量,需要在变量前面声明这个变量的类型,比如

    • int a = 1;String b = "杜金亮";
      
  • 而在中定义一个变量,只允许在变量前声明两种关键字:valvar

    • val(value的简写)用来声明一个不可变的量,这种变量在初始赋值之后就再也不能重新赋值,对应Java中
      final变量

    • var(variable的简写)用来声明一个可变的变量,这种变量在初始赋值之后仍然可以再被重新赋值,对
      Java中的非final变量

    • val a = 10
      var b = "杨浔"
      
    • 可以看到在声明变量时,并没有为了限定类型,那么他的类型时怎么确定的呢?

      • 这是由于Kotlin的类型推导机制完成的
    • 但是这种类型推导机制并不是万能的,在一些延迟赋值的情况下,Kotlin就无法自动推导他的类型了
      这时候就需要显式的声明变量类型才行,语法如下

    • val a : Int = 100
      
      • 可以看到,此时的是首字母大写的,而Java中的首字母是小写的,这是因为Kotlin已经完全抛弃了Java
        中的基本数据类型,全部使用了对象数据类型,如变成了一个类,拥有自己的方法和继承结构
    • Java和Kotlin数据类型对照表

      • Java基本数据类型Kotlin对象数据类型数据类型说明
        intInt整型
        longLong长整型
        shortShort短整型
        floatFloat单精度浮点型
        doubleDouble双精度浮点型
        booleanBoolean布尔型
        charChar字符型
        byteByte字节型
    • 为什么要使用val关键字?

    • 为了解决Java中final关键字没有被合理使用的问题

      • 在Java中,除非你在主动在变量前声明了final关键字,否则这个变量就是可变的。然而这并不
        一件好事,当项目变得越来越复杂,参与开发的人越来越多时,你永远不知道一个可变的便令
        在什么时候被谁给修改了,即使他原本不应该被修改。这就经常会导致出现一些很难排查的
        题。因此,一个好的变成习惯是,除非一个变量名取而允许被修改,否则都应该给他加上final
        键字
    • 什么使用使用什么时候使用?

      • 永远优先使用声明一个变量,而当无法满足你的需求时再使用

函数

  • 语法

    • fun methodName(param1 : Int, param2 : Int) : Int{ 
          return 0
      }
      
  • 语法糖,当一个函数只有一行时,Kotlin允许我们不必写函数体,可以直接将唯一的一行代码写在函数定义
    尾部,中间用等号连接即可

    • fun largerNum(num1 : Int, num2 : Int) : Int { 
          return max(param1, num2)
      }
      
      ====>
      
      fun largerNum(num1 : Int, num2 : Int) : Int = max(num1, num2)
      
    • 使用这种语法,return关键字也可以省略了,等号足以表达返回值的意思,另外,因为Kotlin优秀的
      型推导机制,所以该函数可以再化简为

    • fun largerNum(num1 : Int, num2 : Int) = max(num1, num2)
      

程序的逻辑控制

if条件控制

  • Kotlin中的if语句和Java中的if语句几乎没有任何区别

    • fun largerNum(num1 : Int, num2 : Int) : Int{ 
          var temp = 0 
          if(num1 > num2){
              temp = num1 
          }else{ 
              temp = num2 
          } 
          return temp
      }
      
  • Kotlin中的if语句相比于Java有一个额外功能——他是可以有返回值的,返回值就是语句每一个条件中最后
    行代码的返回值,因此上面的代码可以简化为

    • fun largerNum(num1 : Int, num2 : Int) : Int{ 
          val temp = if(num1 > num2){
              num1 
          }else{ 
              num2
          } 
      	return temp
      }
      
  • 可以看到temp是一个多余的变量,故可以再次修改

    • fun largerNum(num1 : Int, num2 : Int) : Int{
          return if(num1 > num2){
              num1 
          }else{
              num2 
          }
      }
      
  • 这样看的话,return后面可以看作是一句话,故可以再次修改

    • fun largerNum(num1 : Int, num2 : Int) = if(num1 > num2){
          num1
      }else{
          num2
      }
      
      周知,大括号里有一句以不写大括号=============>
      
      fun largerNum(num1 : Int, num2 : Int) = if(num1 > num2) num1 else num2
      

when条件语句

  • when语句有点类似Java中的switch语句,但他又远比语句强大得多

    • switch有着种种限制,比如他只能传入整型或短于整型或者字符串变量作为条件,其次每一个case条件都要
      最后主动加上一个break,否则执行完当前case之后会一次执行下面的case
  • 下面看一个实例

    • fun getScore(name : String) = if(name == "Tom"){ 
          86
      }else if(name == "Jim"){
          77
      }else if(name == "Jack"){
          100
      }else{
          0
      }
      
  • 当判断条件很多时,使用if就会显得代码很冗余,这时可以使用when

    • fun getScore(name : String) = when(name){ 
          "Tom" -> 86 
          "Jim" -> 77 
          "Jack" -> 100 
          else -> 0
      }
      
  • 因为和if语句一样,也是可以有返回值的,所以仍旧可以使用单行代码函数的语法糖

  • 语句允许传入一个任意类型的参数,然后可以在when的结构提中定义一系列的条件,格式是

    • 匹配值 -> {执行逻辑}
      
    • 当代码只有一行时,{}可以省略

  • 除了精确匹配之外,语句还允许进行类型匹配

    • fun checkNumber(num : Number){
          when(num){ 
              is Int -> println("number is Int") 
              is Double -> println("number is Double") 
              lse -> println("number not support") 
          }
      }
      
    • 上述代码中,关键字是类型匹配的核心,它相当于Java中的instanceof关键字,由于checkNumber()函数接受一个Number类型的参数,这是Kotlin内置的一个抽象类,像Int、Float、Double等与数字相关的类都是他的子类,所以这里就可以使用了类型匹配来判断传入的参数到底属于什么类型

  • when语句的基本用法就这些,但其实when语句还有一种不带参数的用法

    • fun getScore(name : String) = when{
          name == "Tom" -> 86
          name == "Jack" -> 100
          else -> 0
      }
      
    • 这种写法可能会觉得比较冗余,但有些情况必须使用这种写法才能实现,如所有Tom开头的人都是86分:

      • fun getScore(name : String) = when{
            name.startWith("Tom") -> 86
            name == "Jack" -> 100
            else -> 100
        }
        

循环语句

  • 熟悉Java的人都晓得,Java中主要有两种循环语句:while循环和for循环。Kotlin中也提供了whilefor循环,其中while循环不论是语法还是使用技巧上都和Java一样,故不讨论

  • Kotlin在for循环方面做了很大幅度的修改,Java中最常用的for-i循环在Kotlin直接被舍弃,而Java中另一种for-each循环则被Kotlin进行了大幅度的增强,变成了for-in循环

  • 在此之前,先普及一个区间的概念

    • val range = 0..10
      
    • 上述代码表示创建了一个0到10的区间,并且两端都是闭区间,这意味着0到10这两个端点都是包含在区间中的,用数学的方式表示就是[0,10]

    • 其中,..是创建两端闭区间的关键字,在..两边置顶区间的左右端点就可以创建一个区间了

  • 有了区间后就可以通过for-in循环来遍历这个区间

    • for(i in 0..10){
          println(i)
      }
      
  • 在很多情况下,双端闭区间却不如单端闭区间好用,比如数组的下标是从0开始,一个长度为10的数组,他的下标区间范围是0到9,因此左闭右开的区间更加常用,Kotlin中可以使用until关键字来创建一个左闭右开的区间

    • val range = 0 until 10
      
    • for(i in 0 until 10{
          println(i)
      }
      
  • 默认情况下,for-in循环每次执行循环时会在区间回味内递增1,相当于Java for-i循环中i++的效果,而如果你想要跳过其中的一些元素,可以使用step关键字:

    • for(i in 0 until 10 step 2){
          println(i)
      }
      
    • 相当于i+=2的效果

  • 上述两种都是区间左端必须小于区间右端,也就是这两种关键字创建的都是一个升序的区间,如果想创建一个降序的区间,可以使用downTo关键字

    • for(i in 10 downTo 1){
          println(i)
      }
      
    • 相当于[10, 1]

面向对象编程

  • 什么是面向对象编程?
    • 先将实物封装成具体的类,然后将事物所有的属性和能力分别定义成类中的字段和函数,接下来对类进行实例化,再根据具体的变成需求调用类中的字段和方法即可

类与对象

  • class Person{
        var name = ""
        var age = 0
        
        fun eat(){
            println(name + "is eating. He is " + age + "years old.")
        }
    }
    
    val p = Person()
    
    ====================================
    
    
    fun main(){
        val p = Person()
        p.name = "Jack"
        p.age = 21
        p.eat()
    }
    
  • 相对于Java,省去了关键字new

继承与构造函数

  • class Student{
        var id = ""
        var grade = 0
    }
    
  • 想要让Student类继承Person类需要做两件事

    • 第一,使Person类可以被继承

      • 在Kotlin中,任何一个非抽象类默认都是不可以被继承的,相当于Java中给类声明了final关键字,之所以这样设计和val关键字的原因是差不多的,因为类和变量一样,最好都是不可变的,而一个类允许被继承的话,他无法预知子类会如何实现,因此可能就会存在一些未知的风险。

      • open class Person{
            ....
        }
        
    • 第二,让Student类继承Person类

      • 与Java不同,继承使用的关键字是:而不是extends

      • class Student : Person(){
            var id = ""
            var grade = 0
        }
        
  • 为什么继承的Person后面有括号?

    • 在Java的继承中,我们知道在子类的构造函数中是要调用父类的构造函数的,但是在Kotlin中,构造函数分为主构造函数和次构造函数,最常用的是主构造函数

    • 主构造函数的特点是没有函数体,直接定义在类名的后面即可

      • class Student(val id : String, val grade : Int) : Person(){
            
        }
        
      • 因为主构造函数没有函数体,所以如果想在主构造函数中编写一些逻辑,可以在init结构体中书写

        • class Student(val id : String, val grade : Int) : Person(){
              init {
                  println("id is " + id)
                  println("grade is " + grade)
              }
          }
          
      • 根据继承特性的规定,子类的构造函数必须调用父类的构造函数,可是主构造函数并没有函数体,我们怎样去调用父类的构造函数呢?你可能会说,在init结构体中去调用不就好了。这或许是一种办法,但是在绝大多数场景下,我们是不需要编写init结构体的。在Kotlin中子类的主构造函数调用父类中的哪个构造函数,在继承的时候通过括号指定

        • class Student(val id : String, val grade : Int) : Person(){
              
          }
          
      • 上述为父类无参数的构造函数,而如果对其进行修改

        • class Person(val name : String, val age : Int) {
              
          }
          
        • 这时子类就会出错,需修改为

          • class Student(val id : String, val grade : Int, name :String, age : Int) : Person(name, age)
            
        • 需要注意的是,在Student类的主构造函数中增加name和age这两个字段时,不能再将他们声明成val,因为在主构造函数中声明成valvar的参数将自动成为该类的字段,,这就会导致和父类同名的name和age字段造成冲突。因此这里的name和age参数前面我们不用加任何关键字,让他的作用于仅限定在主构造函数中即可

    • 次构造函数

      • 任何一个类只能有一个主构造函数,但是可以有多个次构造函数,次构造函数都必须调用主构造函数(包括间接调用)

        • class Student(val id : String, val grade : Int, name : String, age : Int) : Person(name, age){
              constructor(name : String, age : Int) : this("", 0, name, age){
                  
              }
              
              constructor() : this("", 0){
                  
              }
          }
          
          
          ==================================
          
          
          val student1 = Student()
          val student2 = Student("Jack", 19)
          val student3 = Student("a123", 5, "Jack", 18)
          
      • 还有一种特殊的情况:类中只有次构造函数,没有主构造函数,即当一个类没有显式定义主构造函数且定义了次构造函数时,他就是没有主构造函数的

        • class Student : Person{
              constructor(name : String, age : Int) : super(name, age){
                  
              }
          }
          
        • 可以看到这里的Person没有后面的括号了,这是为什么呢?

          • 回顾之前有主构造函数时为什么需要加括号,因为主构造函数没有函数体,所以在主构造函数初始化时,调用父类的哪个构造函数由类名后的括号来指定,而这里没有主构造函,只有次构造函数,而又因为次构造函数必须调用主构造函数,所以此时他需要使用super关键字调用父类的构造函数,所以在类名后面就可以不用指定调用哪个构造函数了,因为在次构造函数那里已经指定过了

接口

都一样,随缘

可见性修饰

修饰符JavaKotlin
public所有类可见所有类可见(默认)
private当前类可见当前类可见
protected当前类、子类、同一包路径下的类可见当前类、子类可见
default同一包路径下的类可见(默认)
internal同一模块中的类可见

数据类与单例类

  • 数据类,就是持久化类、领域类

    • Kotlin只需要在类前加data关键字即可,其他的都不用写

    • data class User(val username : String, val password : String)
      
    • 当一个类没有任何代码时,可以将大括号省略

  • 单例类,某个类在全局只能有一个实例,私有一个静态实例,写一个get方法,实例化的时候判断实例是否为空,不为空就返回,为空赋值返回

    • Kotlin只需要把class换为object即可

    • 调用类似Java的静态方法的调用,实际上Kotlin在背后自动创建了一个Singleton的实例,并且保证全局只会存在一个Singleton实例

    • Object Singleton{
          fun singleton(){
              println("singleton is called.")
          }
      }
      

Lambda编程

  • 集合的创建和遍历

    • List

      • # 不可变集合
        val list = listOf("Apple", "Banana", "Orange")
        
        for(fruit in list){
            println(fruit)
        }
        
        #可变集合
        val list = mutableListOf("Apple", "Banana", "Orange")
        list.add( "Pear")
        
        for(fruit in list){
            println(fruit)
        }
        
    • Set

      #不可变集合
      val set = setOf("Apple", "Banana", "Orange")
      
      for(fruit in set){
          println(fruit)
      }
      
      #可变集合
      val set = mutableListOf("Apple", "Banana", "Orange")
      list.add( "Pear")
      
      for(fruit in set){
          println(fruit)
      }
      
    • Map

      • #存数据
        map["Apple"] = 1
        #取数据
        val number = map["Apple"]
        
        #不可变集合
        val map = mapOf("Apple" to 1, "Banana" to 2)
        #可变集合
        val map = mutableMapOf("Apple" to 1, "Banana" to 2)
        map["Pear"] = 3
        
        for((fruit, number) in map){
            println("fruit is" + fruit + ",number is " + number)
        }
        
  • 集合的函数式API

    • Lambda的语法结构

      • {参数名1 : 参数类型, 参数名2 : 参数类型 -> 函数体}
        
    • 集合的函数式API实质上就是接受了一个Lambda参数

      • #按给定条件查询最大值
        val maxLengthFruit = list.maxBy{ it.length }
        #按给定条件将集合中的每个元素映射成另外一个值,最终生成一个新的集合
        val newList = list.map{ it.toUppercase }
        #按给定条件过滤,最终生成一个新的集合
        val newList = list.filter{ it.length > 4}
        #判断集合中是否至少存一个元素满足条件
        val flag = list.any{ it.length > 5 }
        #判断集合中所有元素是否都满足条件
        val flag = list.all{ it.length > 5 }
        
    • Lambda简化条件

      • 当Lambda参数是函数最后一个参数时,可以将Lambda表达式移到函数括号外面
      • 当Lambda参数是函数的唯一一个参数的话,还可以将函数的括号省略
      • 因为Kotlin的类型推导机制,Lambda的参数列表在大多数情况下不必声明参数类型
      • 当Lambda表达式的参数列表只有一个参数时,也不必声明参数名,而是可以使用it关键字来代替
  • Java函数式API的使用

    • 函数式编程,自己看去

空指针检查

  • Kotlin通过编译时判空检查的机制几乎杜绝了空指针异常

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

    • 一旦传入一个null参数则会报错
  • 如果需要设定某个参数可以接受空呢?

    • 可以使用?

    • fun doStudy(study : Study?){
      	if(study != null){
              study.readBooks()
          }
      }
      
    • 但是需要注意的是,一旦参数可以为空,那么下方的调用则必须判断是否为空,不为空才可以调用方法,否则会报错

  • 如果每个方法都要写判断语句的话,会很繁琐,所以Kotlin提供了一系列的辅助工具

    • ?.

      • 就是当对象不为空时正常调用,为空则什么都不做

      • fun doStudy(study : Study?){
            study?.readBooks()
        }
        
    • ?:

      • 就是当左边表达式不为空就返回左边表达式的结果,反则就返回右边表达式的结果

      • fun getTextLength(text : String?) = text?.length ?: 0
        
    • !!

      • 就是非空断言,表示你非常确信这里的对象不会为空,但是在使用断言时,最好还是问下自己还有没有更好的方案

      • 在这里虽然在main方法判空了,但是在调用toUpperCase()时还会认为这里存在风险,编译不通过

      • var content : String? = "hello"
        
        fun main(){
            if(content != null){
                printUpperCase()
            }
        }
        
        fun printUpperCase(){
            val upperCase = content.toUpperCase()
            println(upperCase)
        }
        
    • let

      • obj.let{obj2 -> 
        	//编写具体的业务逻辑       
        }
        
      • 调用了obj的let函数,然后Lambda表达式中的代码就会立即执行,并且这个obj对象本身还会作为参数传递到Lambda表达式中,不过为了防止变量重名,这里将参数改成了obj2,但实际上他们是一个对象,这就是let的作用

      • 重新审视下这个代码

        • fun doStudy(study : Study?){
              study?.readBooks()
              study?.sleep()
          }
          
      • 变化成原始代码就是这个样子

        • fun doStudy(study : Study?){
              if(study != null){
              	study.readBooks()
              }
              if(study != null){
              	study.sleep()
              }
          }
          
      • 可以看到对对象的判断出现了两次,这无疑是冗余的,使用let修改

        • fun doStudy(study : Study?){
              study?.let{ stu ->
                   stu.readBook()
                   stu.sleep()
              }
          }
          
      • 因为若表达式的参数列表只有一个参数时,可以不用声明参数名,用it替换

        • fun doStudy(study : Study?){
              study?.let{ 
                   it.readBook()
                   it.sleep()
              }
          }
          
      • 需要补充的一点是,对于全局变量的判空问题,可以使用let,但是使用if时仍旧会出错,这是因为全局变量的值随时都有可能被其他线程修改,即使做了判空处理,仍然无法保证if与剧中的study变量没有空指针风险

补充

字符串内嵌表达式

  • val name = "杜金亮"
    
    println("hello world" + name)
    
    println("hello world ${name}")
    
    //当表达式内只有一个变量时,大括号可以省略
    println("hello world $name")
    

函数的参数默认值

fun printParams(num : Int, str : String = "hello")
printParams(123)

fun printParams(num : Int = 100, str : String = "hello")
//若想要使num使用默认值,可以通过键值的方式赋值
printParams(str = "world")
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-07-15 16:18:51  更:2021-07-15 16:19:21 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/2 6:52:12-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码