kotlin基础语法学习
简单的入个门, 复杂的操作放在下一章节
kotlin 包
- kotlin包和java的包不同, java类必须放在对应包名的目录下, 而kotlin则没有强制要求
- kotlin的类名也可以和文件名不同
- kotlin甚至可以不用写类
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字符串模板的使用方式
$变量
直接输出变量的值${表达式}
在花括号内可以写上表达式
上面那段字符串操作模板被反编译成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/varfun sum(a: Int, b: Int)
可能是因为它不用 get/set 方法. 不过可变参数需要主动声明vararg
, 例如:ListOf<String>(vararg arr: String)
- 属性
- 属性和字段的区别
①在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 表达式
区间, 数列和循环
- 区间
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
复制代码
- 迭代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()
}
复制代码