前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Kotlin学习笔记

Kotlin学习笔记

作者头像
用户9854323
发布2022-06-25 11:01:39
1.9K0
发布2022-06-25 11:01:39
举报
文章被收录于专栏:小陈飞砖

文章目录

一. Kotlin 基础知识

1.1 函数结构:

区块式体:

代码语言:javascript
复制
fun max(a: Int, b: Int): Int {
    return if (a > b) a else b
}

上式可以写成如下表达式体(Kotlin 会通过类型推导来得知该表达式的类型):

代码语言:javascript
复制
fun max(a:Int, b:Int) = if (a > b) a else b
1.2 变量

Kotlin 定义变量的语法为: var/val name:Type

  • var (variable ),可以修改
  • val ( value ),不能修改
代码语言:javascript
复制
var age : Int = 17    // 定义一个可以被修改的变量
val ID : String= "1000"    // 定义一个不可修改的变量
// 还可以省略变量类型,Kotlin会类型推导出变量的类型
var age = 17
val id = "1000"

注意:val 表示该变量 引用不可变,但是对象里的内容可以变

1.3 when、循环语句
1.3.1 when

在 Java 中有 switch 语句,在 Kotlin 中使用 when 来代替 switch

代码语言:javascript
复制
when(参数){
    参数1 -> {
        //...
    }
    参数1 -> {
        //...
    }}
1.3.2 循环语句

Kotlin 中的 while 和 do…while 循环和 Java 没有区别:

代码语言:javascript
复制
while (condition) {
    //
}
do {
    //
} while (condition)

for 循环和 Java 中的是区别:

代码语言:javascript
复制
// Java for 循环
for (int i = 0; i <= 100; i++) {
    System.out.println(i);
}
// 对应 Kotlin 版本
for(i in 0..100){
    println(i)
}

1)、闭区间:使用 … 操作符 表示一个区间,该区间是闭区间,包含开始和结束的元素,然后使用 in 操作符来遍历这个区间 2)、半闭区间 :即只包含头部元素,不包含尾部:until

代码语言:javascript
复制
for(i in 0 until 100){
    println(i)
}

3)、倒序遍历:downTo(闭区间)

代码语言:javascript
复制
for(i in 100 downTo 0){
    println(i)
}

4)、步长:遍历的时候 步长(step) 默认是 1,可以通过 step 关键字来指定步长

代码语言:javascript
复制
for( i in 100 downTo 0 step 2){
    println(i)
}
1.4 Kotlin 异常处理

1)、throw 关键字在 Kotlin 中是 表达式,有返回值

代码语言:javascript
复制
val percentage = if (number in 0..100)
        number
    else
        throw IllegalArgumentException(
                "A percentage value must be between 0 and 100: $number")

2)、Kotlin 中可以不必向上抛出异常(java中必须抛出,不然编译不过)

代码语言:javascript
复制
fun readNumber(reader: BufferedReader): Int?{
    try {
        val line = reader.readLine()   // throws IOException
        return Integer.parseInt(line)  // throws NumberFormatException
    } catch (e: NumberFormatException) {
        return null
    } finally {
        reader.close()   // throws IOException
    }
}

java中则:

代码语言:javascript
复制
int readNumber( BufferedReader reader) throws IOException {
    try {
        String line = reader.readLine(); // throws IOException
        return Integer.parseInt(line);   // throws NumberFormatException
    } catch (NumberFormatException e) {
        return -1;
    } finally {
        reader.close(); // throws IOException
    }
}
1.5 “?” 和 “!!”

1. 声明对象时(包括方法的参数):

  • 把"?"跟在类名后面,表示这个对象允许为null;
  • 把"!!"跟在类名后面,表示这个对象不允许为null; 2. 调用对象时:
  • 把"?"跟在对象后面,表示如果为null,程序就会视而不见,不会空指针。
  • 把"!!"跟在对象后面,表示如果为null,那么系统会报异常。
代码语言:javascript
复制
  // 这是声明一个变量,问号跟在类名后面
    var room: Room? = Room()
    private fun checkRoom() {
        // 因为加上了问号,所以可以任意的把room变成空
        room = null
        // 因为在调用时加上了问号,所以程序不会抛出异常
        Log.d("TAG", "-->> room name = ${room?.roomName}")
    }

方法参数也和声明一个变量类似

代码语言:javascript
复制
fun test(){
    heat(null)     //编译过
    heat1(null)    //编译不过
}

//参数str可以传null
fun heat(str: String?): String{
     return str + "热"
}

//参数str 不可以传null
fun heat1(str: String): String{
    return str + "热"
}
代码语言:javascript
复制
var str: String? = "ds";
var length = str?.length;

var str2: String? = null
var length2 = str2?.length;

var str1: String = "ds";
var length1 = str1!!.length;

var str3: String? = null
var length3 = str3!!.length;

//编译后
String str = "ds";
int length = str.length();

String str2 = (String)null;
Integer length2 = null;

String str1 = "ds";
int length1 = str1.length();

String str3 = (String)null;
Intrinsics.throwNpe();
int length3 = str3.length();
  1. 如果不用"?"
代码语言:javascript
复制
  // 这样程序就默认的给room加上了!!,从此以后room不允许为null
    var room: Room = Room()
    private fun checkRoom() {
        // 当把null赋给room时,编译不会通过
        room = null
        // 并且编译器建议把对象后面的问号删除,因为这个对象永远不为空
        Log.d("TAG", "-->> room name = ${room.roomName}")
    }
  1. 定义变量时"?"的使用
代码语言:javascript
复制
  val room: Room? = Room()    // 先实例化一个room,并且room可以为空
    val room: Room? = null      // 不实例化了,开始room就是空的

    val room: Room = Room()   // 实例化一个room,并且room永远不能为空
    val room = Room()         // 和上一行代码一样,是简写语法
  1. 使用了 “?” 就真的不会NullPointerException了么?
代码语言:javascript
复制
  val roomList: ArrayList<Room>? = null
   if (roomList?.size > 0) {
        Log.d("TAG", "-->> 房间数不是0")
   }

编译器会告诉我们:当roomList为null的时,它的size返回就是"null",但是"null"不可以和int值比大小,所以编译器建议我们写成roomList?.size!! > 0,但这样一定会报NullPointerException。

那是不是必须得在外面套一层if(roomList != null)这种Java常见语句才能避免异常吗? 不过Kotlin不会让程序出现这种啰嗦的代码,所以里面提供了对象A ?: 对象B表达式, ?:表示的意思是,当对象A值为null的时候,那么它就会返回后面的对象B,所以可以写为:

代码语言:javascript
复制
 val roomList: ArrayList<Room>? = null
    if (roomList?.size ?: 0 > 0) {    // 这一行添加了?:
        Log.d("TAG", "-->> 房间数不是0")
    }

就目前为止使,用上面的?和?:基本上能避免程序中出现的所有NullPointerException。

  1. 如果变量可以为null(使用操作符"?"),则编译后是包装类型
代码语言:javascript
复制
//因为可以为 null,所以编译后为 Integer
var width: Int? = 10
var width: Int? = null


//编译后的代码
@Nullable
private static Integer width = 10;
@Nullable
private static Integer width;

//再来看看方法返回值为整型:
//返回值 Int 编译后变成基本类型 int
fun getAge(): Int {
    return 0
}
//返回值 Int 编译后变成 Integer
fun getAge(): Int? {
    return 0
}
1.6 重载调用函数

假设我们有如下的函数:

代码语言:javascript
复制
fun <T> joinToString(collection: Collection<T>,
    separator: String, 
    prefix: String, 
    postfix: String): String

1、调用(为参数值指定参数名称):

代码语言:javascript
复制
joinToString(collection, separator = " ", prefix = " ", postfix = ".")

2、为函数参数指定默认值:

代码语言:javascript
复制
fun <T> joinToString(collection: Collection<T>, 
   separator: String = ", ", 
    prefix: String = "", 
    postfix: String = "" ): String

3、重载调用(有默认值的情况下才可以)

代码语言:javascript
复制
joinToString(list)
joinToString(list, prefix = "# ")
1.7 顶级函数和属性(静态的)

在 Java 中我们需要把函数和属性放在一个类中,在 Kotlin 中我们可以把某个函数或属性直接放到某个 Kotlin 文件中,把这样的函数或属性称之为 顶级函数或属性。

例如在 join.kt 文件中:

代码语言:javascript
复制
package strings
fun joinToString(...): String { 
    ... 
}

1)、但是在 Java 代码中如何调用该方法呢? 因为 JVM 虚拟机只能执行类中的代码,所以 Kotlin 会生成一个名叫 JoinKt 的类,并且顶级函数是静态的,所以可以在 Java 中这样调用顶级函数:

代码语言:javascript
复制
JoinKt.joinToString(...)

2)、在Kotlin中如何调用,如果在不同的包,需要把这个顶级函数导入才能调用:

代码语言:javascript
复制
//相当于 import strings.JoinKt.joinToString
import strings.joinToString 
//相当于 import strings.JoinKt.*
import strings.* 

3)、顶级属性 同样也是 static 静态的 如果使用 var 来定义会生成对应的静态setter、getter函数 如果使用 val 来定义只会生成对应的静态getter函数

4)、Kotlin文件名被修改怎么办? 如果所在的Kotlin文件名被修改,编译生成的类名也会被修改,可以通过注解的方式来固定编译生成的类名:

代码语言:javascript
复制
@file:JvmName("StringFunctions")
package stringsfun joinToString(...): String { 
    ... 
}
代码语言:javascript
复制
   调用的时候:
代码语言:javascript
复制
import strings.StringFunctions; 

StringFunctions.joinToString(list, ", ", "", "");
1.8 可变参数 和 展开操作符

1)、可变参数,可传递任意数量参数 java中使用…来声明可变参数,如:

代码语言:javascript
复制
public static <T> List<T> listOf(T... items) {
    System.out.println(items.getClass()); //数组类型
    return Arrays.asList(items);
}

Kotlin 和 Java 不一样,Kotlin 使用 vararg 关键来定义可变参数:

代码语言:javascript
复制
fun <T> listOf(vararg items: T): List<T> {
    println(items.javaClass)     //数组类型
    return Arrays.asList(*items) // * spread operator
}

2)、展开操作符 通过上面的两段代码比较我们发现:Kotlin 需要显示的将可变参数通过 * 展开,然后传递给 asList 函数。这里的 * 就是 展开操作符,在 Java 中是没有 展开操作符 的。

  • 不使用展开操作符时:
代码语言:javascript
复制
val intArr: Array<Int> = arrayOf(1, 2, 3, 4)
Arrays.asList(0, intArr).run {
    println("size = $size")}
//输出结果:
size = 2
  • 使用展开操作符时:
代码语言:javascript
复制
val intArr: Array<Int> = arrayOf(1, 2, 3, 4)
Arrays.asList(0, *intArr).run {
    println("size = $size")}
//输出结果:
size = 5
1.9 中缀调用infix

使用关键字 infix 修饰的函数都能够 中缀调用, 被关键字 infix 修饰的函数只能有一个参数。

Kotlin 中的 to 就是一个中缀函数:

代码语言:javascript
复制
public infix fun <A, B> A.to(that: B): Pair<A, B> = Pair(this, that)

下面我们来对比下 to 函数的常规调用和中缀调用:

代码语言:javascript
复制
1.to("one")  //普通的函数调用
1 to "one"   //函数的中缀调用

除了 to 函数,还有我们介绍 循环 的时候讲到的 until、downTo、step 也是中缀函数:

代码语言:javascript
复制
public infix fun Int.until(to: Int): IntRange {
    if (to <= Int.MIN_VALUE) return IntRange.EMPTY
    return this .. (to - 1).toInt()
}

public infix fun Int.downTo(to: Int): IntProgression {
    return IntProgression.fromClosedRange(this, to, -1)
}

public infix fun IntProgression.step(step: Int): IntProgression {
    checkStepIsPositive(step > 0, step)
    return IntProgression.fromClosedRange(first, last, if (this.step > 0) step else -step)
}

//使用示例:
for(i in 0 until 100){}

for (i in 100 downTo 0 step 2) {}
1.10 本地函数

本地函数(local function) 是在函数里面定义函数,本地函数只能在函数内部使用 什么时候使用本地函数?当一个函数里的逻辑很多重复的逻辑,可以把这些逻辑抽取到一个本地函数。

代码语言:javascript
复制
fun saveUser(user: User) {
    if (user.name.isEmpty()) {
        throw IllegalArgumentException("Cannot save user ${user.id}: Name is empty")
    }
    if (user.address.isEmpty()) { 
        throw IllegalArgumentException("Cannot save user ${user.id}: Address is empty")
    }
}

这个 saveUser 函数里面有些重复逻辑,如果 name 或 address 为空都会抛出异常 可以使用本地函数优化下:

代码语言:javascript
复制
fun saveUser(user: User) {
    fun validate(value: String, fieldName: String) { 
        if (value.isEmpty()) {
            throw IllegalArgumentException("Can't save user ${user.id}: " + "$fieldName is empty")
        }
    }
    validate(user.name, "Name") 
    validate(user.address, "Address") 
}

本地函数避免了模板代码的出现。如果不使用本地函数,我们需要把 validate函数 定义到外面去,但是这个函数只会被 saveUser函数 使用到,从而污染了外面的全局作用域。通过本地函数使得代码更加清晰,可读性更高。 注意:虽然 Kotlin 允许在函数内部定义函数,但是不要嵌套太深,否则会导致可读性太差

1.11 访问修饰符
  1. 类访问修饰符如下:
在这里插入图片描述
在这里插入图片描述
  1. 类成员访问修饰符:
  • 只有 ‘’protected‘’ 对应的 ‘’Kotlin可访问级别‘’ 有区别:只有子类可访问

1.12 声明类的几种方式

  1. class className : 这个类是 public final 的
代码语言:javascript
复制
class Person
编译后:
public final class Person {
}
  1. class className([var/val] property : Type…)
  • 1、会生成一个构造方法,参数就是括号里的那些参数
  • 2、会根据括号的参数生成对应的属性
  • 3、会根据 val 和 var 关键字来生成 setter、getter 方法,var 表示该属性可以修改;val 表示该属性不能被修改
代码语言:javascript
复制
class Person(val name: String) //name属性不可修改

---编译后---

public final class Person {
   //1. 生成 name 属性
   @NotNull
   private final String name;

   //2. 生成 getter 方法
   //由于 name 属性不可修改,所以不提供 name 的 setter 方法
   @NotNull
   public final String getName() {
      return this.name;
   }
   
   //3. 生成构造函数
   public Person(@NotNull String name) {
      Intrinsics.checkParameterIsNotNull(name, "name");
      super();
      this.name = name;
   }
}
  1. data class className([var/val] property: Type)
  • 新建 bean 类的时候,常常需要声明 equals、hashCode、toString 等方法,我们需要写很多代码。
  • 在 Kotlin 中,只需要在声明类的时候前面加 data 关键字就可以完成这些功能。
  • 注意 : 哪些属性参与 equals、hashCode、toString 方法呢?primary constructor 构造函数里的参数,都会参与 equals、hashCode、toString 方法里。
  1. object className 这种方法声明的类是一个单例类,以前在Java中新建一个单例类,需要写一些模板代码,在Kotlin中一行代码就可以了(类名前加上object关键字)。
  2. 内部类
  • 在 Kotlin 中内部类默认是静态的( Java 与此相反),不持有外部类的引用:
代码语言:javascript
复制
class OuterClass {
    //在 Kotlin 中内部类默认是静态的,不持有外部类的引用
    class InnerStaticClass{
    }
    //如果要声明非静态的内部类,需要加上 inner 关键字
    inner class InnerClass{
    }
}

编译后代码如下:
class OuterClass {
   public static final class InnerStaticClass {
   }
   public final class InnerClass {
   }
}

1.13 静态变量和静态方法

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-04-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 一. Kotlin 基础知识
    • 1.1 函数结构:
      • 1.2 变量
        • 1.3 when、循环语句
          • 1.4 Kotlin 异常处理
            • 1.5 “?” 和 “!!”
              • 1.6 重载调用函数
                • 1.7 顶级函数和属性(静态的)
                  • 1.8 可变参数 和 展开操作符
                    • 1.9 中缀调用infix
                      • 1.10 本地函数
                        • 1.11 访问修饰符
                        领券
                        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档