Kotlin基础语法学习笔记
mian方法例子
fun main(){
println("Hello World");
}
变量
val定义只读变量
var定义可变变量
顶层变量-定义在main函数外面的变量,类似Java中的静态变量,但是没有跟类强关联,可直接调用,不用通过类名去调用
val PI = 3.14; // top level变量
fun main() {
val popcorn = 5 // 不可变变量
val hotdogs = 10
var customer = 10 //可变变量
customer = 8
println(customer)
println(PI)
}
字符串模板
字符串模板表达式以$开始,如果需要计算可以使用{}
var customer = 10;
println("Customer: $customer");
println("Customer: ${customer + 1}");
基本数据类型
kotlin的每个变量和数据结构都有类型。Kotlin编译器会通过类型推断来确定变量的类型
总共有13种基本数据类型
整数
Byte/Short/Int/Long
val year: Int = 20;
无符号整数
UByte/UShort/UInt/ULong
val scope: UInt = 100u;
浮点数
Float/Double
val tmp: Float = 1.0f
val tmp2: Double = 2.0
//val tmp2: Double = 2.0d 不能加d
布尔值
Boolean
val flag: Boolean = true;
字符
Char
val c: Char = 'a'
字符串
val str: String = "Hello"
声明未初始化变量
必须用":"指定类型,否则编译报错
必须在使用之前赋值,否则编译报错
val a: Int
a = 5
集合
有三种集合类型:List/Set/Map
List:有序集合
Set:独特的无序集合
Map:键值对,键唯一,每个键只映射到一个值
每种集合类型都是可变的和只读的
List
按添加顺序存储,允许重复项
只读列表用listOf函数创建
可变列表用mutableListOf函数创建
val readOnlyShapes = listOf("triangle", "square", "circle")
val mutableShapes: MutableList<String> = mutableListOf("triangle", "square", "circle")
//为防止不期望的改变,可以将可变列表转换为可读不可变的列表,类型转换
//转变的原理是通过接口继承关系隐藏了Mutable集合的可变能力,其实底层还是一个可变集合对象,能通过原来的引用去调用它的可变方法。而反过来List->Mutable则不行,原因是List是Mutable的父接口,不可变转可变不安全,List本质是可不变的,假如转了之后没有可变的能力
val shapesLocked: List<String> = mutableShapes
List是有序的,可用索引下标访问
println(readOnlyShapes[0])
println("${readOnlyShapes[0]}")
//获取列表的第一个元素
readOnlyShapes.first()
//获取列表的最后一个元素
readOnlyShapes.last()
//统计列表中元素数量
readOnlyShapes.count()
//判断某个元素是否存在列表中
println("a" in readOnlyShapes)
//可变列表添加和删除元素
mutableShapes.add("a")
mutableShapes.remove("a")
Set
无序且不可重复
//创建只读Set
val readOnlyFruits = setOf("apple", "banana", "orange")
//创建可变Set
val mutableFruits = mutableSetOf("apple", "banana", "orange")
//显示声明Set内元素类型
val mutableFruits2: MutableSet<String> = mutableSetOf("apple", "banana", "orange")
//Set同样可以从可变转为只读
val readOnlyFruits3: Set<String> = mutableFruits2
//同样用in判断元素是否存在, 还有count add remove等方法
Map
映射,键值对
//创建只读映射
val map = mapOf("apple" to "banana", "orange" to "apple")
//创建可变映射
val map2 = mutableMapOf("apple" to "banana", "orange" to "apple")
//显式声明Map键值对类型
val map3: MutableMap<String, Int> = mutableMapOf("apple" to 1, "orange" to 2)
//可变Map转换为只读Map
val map4: Map<String, Int> = map3
//用键取值
println(map3["apple"])
//如果键不存在,返回null,但是没报空指针异常
println(map3["apple2"])
//可用索引访问[]符向Map中添加键值
map3["abc"] = 100
//移除用remove函数
map3.remove()
//count统计键值对数量
println(map3["abc"])
//containskey判断某个键是否存在Map中
map3.containsKey("abc")
//获取Map的键集合或者值集合,直接.keys或者.values属性即可
println(map3.keys)
println(map3.values)
//用in判断键是否在映射中
"apple" in map3
"apple" in map3.keys
//用in判断值是否在映射中
100 in map3.values
条件表达式
if和when
推荐使用when
var num: Int
val check = true
if(check) {
num = 1
}else{
num = 0
}
println(num)
Kotlin中没有三元运算符,但可用if表示
if(a > b) a else b
当有多个分支时使用when
val name3 = "Java"
when (name3) {
"Java" -> println("gun")
"Kotlin" -> println("coming")
else -> println("unknown")
}
//检查到第一个满足条件的分支就执行,并且跳过后续的分支,类似Java中带break的switch
//when可以返回值
val name3 = "Java"
val result = when (name3) {
"Java" -> "gun"
"Kotlin" -> "coming"
else -> "unknown"
}
println(result)
//可以没有主语(参数)
val result = when {
"Java" == name3 -> "gun"
"Kotlin" == name3 -> "coming"
//注意else也要加->
else -> "unknown"
}
注意:when下各个分支要统一返回类型,虽然在混合String和Int返回时Kotlin的类型推导会把when表达式的返回值类型判断为它们的父类Any,从而没有编译错误,但是混合类型返回代码可读性差,且不安全
范围
1…4相当于1,2,3,4
1…<4相当于1,2,3 左闭右开
4 downTo 相当于4,3,2,1 逆序
1…5 step 2 相当于1,3,5 自定义步长为2
Char也可以用区间,比如:
‘a’…‘d’相当于’a’,‘b’,‘c’,‘d’
‘z’ downTo ‘s’ step 2 相当于’z’ ,‘x’, ‘v’,‘t’
循环
for
for (number in 1..5){
print(number)
}
while
var cakesEaten = 0
while(cakesEaten < 5){
println("eat $cakesEaten")
cakesEaten++;
}
do{
println("eat $cakesEaten")
cakesEaten++;
}while(cakesEaten < 10)
函数
Kotlin函数的特点:
参数必须有类型,用逗号分隔
返回类型写在函数的()之后,用冒号分隔
关键字return用于推出函数或者返回某个值
fun sum(a: Int, b: Int): Int {
return a + b
}
命名参数
在传参时指定参数名,可以按随意顺序传参
fun main() {
printMessageWithPrefix(prefix = "Sean", message = "Hello World")
}
fun printMessageWithPrefix(message: String, prefix: String) {
println("[$prefix] $message")
}
默认参数值
在函数定义时可以定义默认值
fun printMessageWithPrefix(message: String, prefix: String = "Info") {
println("[$prefix] $message")
}
无返回值的函数
如果函数没有返回值,其实返回的类型是Unit
fun printMessage(message: String){
println(message)
}
单表达式函数
省略{},用=号来声明函数体,省略返回类型
fun sum(a: Int, b: Int) = a + b
如果使用{}则必须声明返回类型,除了Unit外
提前返回
用return提前返回值,跟Java一样
lambda表达式
val upperCaseString = { text: String -> text.uppercase() }
println(upperCaseString("hello"))
->之前是参数
->之后是函数体
使用=把函数表达式赋值给了变量upperCaseString
lambda传递给另一个函数
Kotlin中lambda对函数的最后一个参数是lambda表达式的优化:可以将lambda表达式拿出()外,如果同时这个函数没有其他参数,可以省略掉()
val positives = numbers.filter { x -> x > 0 }
.map函数,用来转换集合中的元素
"it"是Kotlin提供的隐式参数,在函数只有一个参数的时候可以使用它,不能自定义名称
val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
val doubleD = numbers.map { it * 2 }
val tripleD = numbers.map { it * 3 }
println(doubleD)
println(tripleD)
函数类型
“函数的形状”
包括参数类型,返回值类型,能执行的操作
函数类型的语法有:
- 每个参数的类型写在括号
()内,并用逗号,分隔。 - 在
->之后的返回类型。
例如:(String) -> String
val upperCaseString: (String) -> String = { str -> str.uppercase() }
你必须要么在 lambda 表达式中声明参数和返回类型,要么作为函数类型声明。否则,编译器将无法知道你的 lambda 表达式的类型。
从函数中返回
lambda表达式可以从函数中返回,前提是必须声明表达式的类型。
以下定义了一个返回函数的函数toSeconds(time: String): (Int) -> Int
fun toSeconds(time: String): (Int) -> Int = when (time){
"hour" -> {value -> value * 60 * 60}
"minute" -> {value -> value * 60}
"second" -> {value -> value}
else -> {value -> value}
}
/**定义了一个toSeconds函数,这个函数接收一个String参数time,根据这个参数time动态的返回函数类型为(Int) -> Int的函数
1.为什么不用val?因为语义上不符合"动态生成转换逻辑的函数",val更加趋向固定值
2.为什么when后的time不需要声明String类型?因为前面已经声明过了,Kotlin不允许这种冗余代码,而且kotlin会根据when的表达式分支条件去推断这个主语time的类型
3.所谓的返回的函数,其实是lambda表达式,需要用{}包起来
**/
fun main() {
val minutes = listOf(10, 20, 30, 40, 50)
val sum = minutes.sumOf(toSeconds("minute"))
println(sum)
}
单独调用
lambda可以在{}后面加()直接调用,()内可以传值
println({text: String -> text.uppercase()}("hello world!"))
尾随lambda
如果lambdad表达式没有参数,可以省略括号()
如果lambda是作为函数的最后一个参数传递,那么可以把lambda放在括号()外面
println(listOf(1, 2, 3).fold(0){x, item -> x + item})
//fold是一个非常灵活的函数,主要用来做累加,拼接字符串的工作,fold意思为折叠,将集合中的元素折叠到初始值initial上,然后返回,使用了泛型,初始值与返回值的类型可以不同,比reduce函数灵活
类
class Customer
属性
在括号()中声明,括号内的内容称为类头部
class Customer(val name: String, val age: Int)
在花括号{}中声明,需要初始值
class Customer(val name: String, val age: Int) {
val id: Int = 0
}
可以使用尾随逗号
class Customer(val name: String,
val age: Int,
val age2: Int,
val age3: Int,
) {
val id: Int = 0
}
创建实例
默认情况下Kotlin会自动创建一个包含类头部声明的参数的构造函数(无参就生成一个无参构造函数)
class Contact(id: Int, var email: String)
val con = Contact(1, "Sean@qq.com");
访问属性
用"."访问
con.email
println("This is my email ${con.email}")
成员函数
class Contact(id: Int, var email: String){
fun sayHello() {
println("Hello")
}
}
数据类
数据类是普通类的增强,额外提供了一些成员函数,方便操作数据,减少了模板代码的编写
比如toString()
equals()==
copy()
data class User(val id: Int, val name: String)
//toString() 普通类没有提供toString()会打印引用,而数据类会打印实例内容
println(user)
//equals() == 比较主构造函数的属性,也就是()内的
//如果包含引用类型成员属性,则会递归的调用引用成员的equals函数继续比较
val user = User(1, "Sean")
val user2 = User(1, "Sean")
println(user.equals(user2)) //true
//copy()浅拷贝,user user2依然共享同一份con引用
val user = User(1, "Sean", con)
println(con)
val user2 = user.copy()
println(user2)
空安全
null安全在编译时检查null值的潜在问题
空安全允许以下操作:
1.明确声明程序在何时允许null
2.检查null值
3.使用安全调用可能包含null值的属性和函数
4.如果监测到null值,声明要采取的行动
可空类型
允许声明的类型具有null值
通过在类型声明后显示添加?来声明
默认情况下类型不允许接受空值
var neverNull: String? = "Hello"
neverNull = null
//neverNull.length 编译报错,不安全,要非空值或者用?.安全调用
检查空值
maybeString != null && maybeString.length > 0
使用安全调用
使用安全调用运算符"?."
如果对象或者其访问的属性为null,则返回null
fun main() {
val nullStr = null
println(lengthString(nullStr))
}
fun lengthString(maybeString: String?): Int? = maybeString?.length
//链式安全调用不会抛出错误,而是返回null
person.company?.address?.country
//安全调用符在调用函数之前判断非空,空值则跳过调用,并返回null
使用Elvis运算符
使用Elvis运算符"?:"来给可能存在null值的变量返回一个默认值
左边是表达式或者变量,右边是默认值
println(nullStr?.uppercase() ?: 0)
//配合return可以提前返回
val nonNullValue = value ?: return // 若 value 为 null,直接返回
更多推荐

所有评论(0)