kotlin基础语法学习

kotlin基础语法学习

简单的入个门, 复杂的操作放在下一章节

kotlin 包

  • kotlin包和java的包不同, java类必须放在对应包名的目录下, 而kotlin则没有强制要求
  • kotlin的类名也可以和文件名不同
  • kotlin甚至可以不用写类

image.png

package base

fun funcMax(a: Int, b: Int): Int {
   return  if(a > b) a else b
}

fun main(args: Array<String>) {
   var a = 10
   var b = 20
   println(funcMax(a, b))
}
复制代码

学过java的配合源码看

public final class FuncDemo01Kt {
   public static final int funcMax(int a, int b) {
      return a > b ? a : b;
   }

   public static final void main(@NotNull String[] args) {
      int a = 10;
      int b = 20;
      int var3 = funcMax(a, b);
      System.out.println(var3);
   }
}
复制代码

看到这里就一目了然了

kotlin编译器把文件FuncDemo01 + Kt 做成了 java 的类名, 而max 是静态方法, 在 kotlin 中被称之为 顶层函数

所以如果在java的类中调用的没有类名的kotlin方法, 可以考虑直接使用 类名.静态函数(FuncDemo01Kt.funcMax)的方式使用它

下面是java调用kotlin方法的方式

package base.java;

import base.FuncDemo01Kt;

public class JavaTestDemo01 {
    public static void main(String[] args) throws Exception {
        int a = 10;
        int b = 30;
        System.out.println(FuncDemo01Kt.funcMax(a, b));
    }
}
复制代码

函数声明和定义

kotlin函数定义使用 fun 关键字完成:

  • 函数体形式
fun sum(a: Int, b: Int):Int {
    return a + b
}
复制代码

fun关键字 + 空格 + 函数名字(参数名: 参数类型, ...): 函数返回值 {函数体}

  • 表达式函数体形式
fun sum(a: Int, b: Int) = a + b
复制代码

fun关键字 + 空格 + 函数名字(参数名: 参数类型, ...) 表达式函数体( = a + b)

// kotlin函数无返回值时使用 `Unit` 关键字, 注意这里和`Uint`做区分, 表示`unsigned int`无符号
fun sum(a: Int, b: Int):Unit {
    // 字符串模板操作关键字 `$`
    println("max = ${if(a > b) a else b}")
}
复制代码

语句和表达式的区别
表达式有值, 可以嵌套使用, 而语句没有, 语句通常都是包围着代码块
在kotlin中 if 语句就是表达式, 它存在返回值, 例如: val max = if(a > b) a else b
在java中 = 赋值操作是表达式, 可以 int a = c = 1; 但在kotlin中赋值操作是语句, 所以不能够

  • 什么是表达式函数体

如果一个函数的整个函数体都是一个表达式时, 我们就可以称之为表达式函数体

fun max(a: Int, b: Int) = if (a > b) a else b
复制代码

其中 = if(a > b) a else b 便是表达式函数体

在表达式函数体中, 我们可以省略 return 关键字, 如上面的 if表达式

  • 可变函数参数
fun printArray(vararg arr: Int) {
    for(v in arr) {
        println(v)
    }
}
复制代码
  • lambda(匿名函数)

在kotlin中凡是被 {} 包裹的函数体都可以称之为 lambda 表达式, 至少我是这样认为的

val max = (Int, Int) -> Int = {x, y -> if(x > y) x else y}
复制代码

在上面源码中, 注意两个部分,
(Int, Int) -> Int 这部分类似于 c++ 中的函数指针, 或者叫函数类型
{x, y -> if(x > y) x else y} 这部分便是 lambda 表达式

变量

val/var 变量名: 变量类型

val 类似于 java 中的 final 变量, 引用不可变变量
var 则是非 final 变量

val v: Int变量 v 声明时, 如果没有进行初始化, 则变量 v 需要在后续中初始化一次

// 如果是声明时, 需要确定类型
val v: Int
v = 10
复制代码

var 定义的变量, 一旦初始化了类型, 下次赋值时该变量的类型不变

var v = 10
v = "hello" // 错误, v的类型已经是 Int 了, 不可能再变成 String
复制代码

字符串格式化模板

fun main(args: Array<String>) {
   var a: Int = 99;
   var b = 11; // 类型推导 为 Int
   println("a = $a, b = $b, a + b = ${a + b}")
}
复制代码

上面这段代码是kotlin字符串模板的使用方式

  1. $变量 直接输出变量的值
  2. ${表达式} 在花括号内可以写上表达式

上面那段字符串操作模板被反编译成java代码时就会变成

public final class StringDemo01Kt {
   public static final void main(@NotNull String[] args) {
      Intrinsics.checkNotNullParameter(args, "args");
      int a = 99;
      int b = 11;
      String var3 = "a = " + a + ", b = " + b + ", a + b = " + (a + b);
      System.out.println(var3);
   }
}
复制代码

看亮点:
String var3 = "a = " + a + ", b = " + b + ", a + b = " + (a + b);

类和属性

在java中类被写成这样的形式:

public class Person {
    private String name;
    private int age;
    
    public final String getName() {
        return this.name;
    }
    
    public final void setName(String name) {
        this.name = name;
    }
    
    public final int getAge() {
        return this.age;
    }
    
    public final void setAge(int age) {
        this.age = age;
    }
    
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
复制代码

使用 idea 的java 转 kotlin 代码的转换器转换成 kotlin 源码后:

class Person(var name: String, var age: Int)
复制代码

看起来舒服多了

在java转化成kotlin后, public 被隐藏, 在kotlin中类是默认public, 而在java中默认是 default (public default protected private)

ps: 在作为类的成员字段, 也是属性所以需要显示的使用 val/var 来确定属性的 set/get 方法class Person(var name: String, var age: Int)而函数参数不需要 val/var fun sum(a: Int, b: Int)可能是因为它不用 get/set 方法. 不过可变参数需要主动声明 vararg, 例如: ListOf<String>(vararg arr: String)

  • 属性
  1. 属性和字段的区别

①在java中:
private String name;成员字段需要另外加上 get/set 方法成为属性, 而在kotlin中的成员字段var name: String是属性, 其底层是定义了成员字段就自动生成该字段的访问器set/get, 在对该字段赋值时. this.property = xxx 在java中调用的是 this.setProperty(xxx), 而读取时this.property在java中调用的是 this.getProperty()

② 在kotlin val/var 声明变量时, 如果使用 val 定义变量, 将被标记成只读属性, 仅生成 get 方法, 不生成set方法, 如果使用的 var 定义变量, 则生成 set/get 方法

当遇到 kotlin 属性 var isDelete时, kotlin会生成 isDelete 方法和 setDeelte 方法, is被替换成 set

  • 自定义属性访问器
class Rectangle(val hight: Int, val width: Int) {
   val isSquare: Boolean
//    get() {
//       return hight == width
//    } // 普通函数体形式
// get() {hight == width} // lamdba形式
   get() = hight == width // 表达式函数体
}
复制代码
  • kotlin中的import关键字

import关键字在kotlin中可以导入类和顶层函数(在java中叫静态函数)

在 kotlin 中写一个扩展函数(就是把this当作参数传递进去的函数)

fun String.lasts(): Char = this[this.length - 1]
复制代码

而在 kotlin中使用 扩展函数 的方法, val ch = "kotlin".lasts()

在 java 中类似写成这样:

// 核心代码在这里, 把 调用 lasts 对象当作参数传递进去了, 这步骤由 kotlin 虚拟机完成
public static final Char lasts(String /* 这个 this就是调用这个方法的对象, 就是前面例子的 "kotlin" */ this) {
    // "kotlin".charAt("kotlin".length() - 1) 类似于这样
    return this.charAt(this.length() - 1);
}
复制代码

先提出一些后面的知识点, 之后还能回头来看看

回到正题, 我们在另一个包里调用 lasts 顶层函数(静态函数), 就会在kotlin上面见到

import base.func.lastChar
复制代码

前面的 base.func 是包名, 而 lastChar 是方法

如果要在另一个包的java中调用, 就变成了

import base.func.ExtensionFuncKt;

public class ExtensionDemo01 {
    public static void main(String[] args) {
        System.out.println(ExtensionFuncKt.lastChar("zhazha"));
    }
}
复制代码

枚举类

enum class Color(val r: Int, val g: Int, val b: Int) {
   RED(255, 0, 0), ORANGE(255, 165, 0), YELLOW(255, 255, 0), GREEN(0, 255, 0), BLUE(
      0,
      0,
      255
   ),
   INDIGO(75, 0, 130), VIOLET(238, 130, 238);
   
   fun rgb() = (r * 256 + g) * 256 + b
   
   fun getWarmth() = when (this) {
      RED, ORANGE, YELLOW -> "warm"
      GREEN -> "neutral"
      BLUE, INDIGO, VIOLET -> "cold"
   }
}
复制代码

借助枚举类学习 when 表达式的使用方法

when 表达式

(1) 使用函数表达式体的形式

fun getWarmth() = when (this) {
   RED, ORANGE, YELLOW -> "warm"
   GREEN -> "neutral"
   BLUE, INDIGO, VIOLET -> "cold"
}
复制代码

(2) 函数体形式

fun getWarmth1(): String {
   when (this) {
      RED, ORANGE, YELLOW -> return "warm"
      GREEN -> return "neutral"
      BLUE, INDIGO, VIOLET -> return "cold"
   }
}
复制代码

智能类型转换和is表达式类型转换


interface Expr {

}

class Num(val value: Int): Expr

class Sum(val left: Expr, val right: Expr): Expr

fun eval(e: Expr): Int {
   if (e is Num) {
      return e.value
   }
   else if (e is Sum) {
      return eval(e.left) + eval(e.right)
   }
   throw IllegalArgumentException("Unknown expression")
}

fun main() {
   println(eval(Sum(Num(1), Sum(Num(2), Num(3)))))
}
复制代码

if (e is Num) if判断返回为 true 时, 则变量 e 被智能转化成 Num 类型

但是使用 is 表达式是有前提的:
该变量必须是 val 定义的变量, 否则无法智能转换, 只能使用 as表达式显示的转换

println((sum.left as Num).value)

val n = e as Num
复制代码

我们还可以使用when 代替 if 表达式

区间, 数列和循环

  1. 区间

kotlin中没有常规的 for 循环 for(int i = 0; i < length; i++) 这种形式, 所以引入区间能够更好的代替这种操作

1..10 区间表达形式, 表示 [1, 10] 之间的数, 包括1 和 10

fun main() {
   val interval: IntRange = 1..10
   for (v in interval) {
      print("$v ")
   }
   
   println()
   for (v in interval.last downTo interval.first step 1) {
      print("$v ")
   }
   println()
   
   // [1, 10)
   for (v in interval.first until interval.last) {
      print("$v ")
   }
}
复制代码
1 2 3 4 5 6 7 8 9 10 
10 9 8 7 6 5 4 3 2 1 
1 2 3 4 5 6 7 8 9 
复制代码
  1. 迭代map
fun main() {
   val map = mutableMapOf(
      1 to "zhazha",
      2 to "haha",
      3 to "haha",
      Pair(4, "xixi"),
      Pair<Int, String>(5, "heihei")
   )
   
   map.forEach(fun(m: Map.Entry<Int, String>): Unit {
      println("key = ${m.key}, value = ${m.value}")
   })
   
   for ((key, value) in map) {
      println("key = $key, value = $value")
   }
   
   // java forEach??? 调用的 java Map 里面的 BiConsumer 接口
   map.forEach { key, value ->
      println("key = $key, value = $value")
   }
   
   // Kotlin forEach??? 调用的 kotlin 的 (key, value) -> {} 遍历 Map 方法
   map.forEach { (key, value) ->
      println("key = $key, value = $value")
   }
   
}
复制代码
fun Set<String>.inSet(str: String) = str in this

val set = setOf<String>("1", "2", "3", "4", "a", "b", "c")

println(set.inSet("a"))

// (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')
fun isLetter(ch: Char) = ch in 'a'..'z' || ch in 'A'..'Z'

// ch >= '0' && ch <= '9'
fun inNumber(ch: Char) = ch in '0'..'9'
复制代码

kotlin 异常

在kotlin中, 异常可以处理也可以不处理, 也不在函数声明上, 也不用写上throws Exception, 抛出异常时也不需要throw new Exception("xxxx")

fun main() {
   val bufferedReader = BufferedReader(InputStreamReader(System.`in`))
   val number = readNumber(bufferedReader)
   println(number)
}

fun readNumber(reader: BufferedReader): Int? = try {
   val line = reader.readLine()
   Integer.parseInt(line)
} catch (e: NumberFormatException) {
   throw e
} finally {
   reader.close()
}
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享