Kotlin语法学习笔记

这篇博客详细介绍了Kotlin编程语言的基础语法,包括类型声明、val和var的区别、高阶函数与Lambda表达式的使用,特别是如何将函数作为参数、如何定义匿名函数以及Lambda的简化语法。此外,还探讨了表达式编程的概念,如枚举类、when表达式、for循环和范围表达式的应用,以及字符串的定义和操作,如原生字符串、模板和判等。通过对Kotlin语法的深入学习,开发者能够更好地理解和应用这门语言。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Kotlin基础语法(笔记)

一、类型声明

变量声明示例:

val a :  String = "i am a person"

小结:Kotlin中声明的类型名通常跟在变量名的后面,并且Kotlin中的所有类型都是大写。

类型推导示例:

val string = "i am kotlin"
val int = 123
val long = 123L
val float = 12.34f
val double = 12.3
val double2 = 10.1e6

小结:Kotlin中大写的才是类型,通过等号给变量赋值可以直接进行类型推导。

函数返回值类型示例

fun sum(x: Int,y: Int): Int {return x + y}  //返回类型为Int

小结:sum函数返回x + y为Int类型,如果去掉返回值类型,函数会默认返回Unit类型,然而实际返回类型是Int,此时就会报错。

表达式函数体示例:

fun sum(x: Int,y: Int) = x + y

小结:以上的写法称为表达式函数体,普通的函数声明称为代码块函数体

fun foo(n: Int) = if (n == 0) 1 else n * foo(n - 1)   
//该函数会报错,说明:  在一些诸如递归的复杂情况下,即使使用表达式定义函数,也必须显式声明类型
// 正确写法    fun foo(n: Int): Int = if (n == 0) 1 else n * foo(n - 1)

总结

  • 作为函数参数时,必须显式声明类型
  • 作为非表达式函数,除了函数返回Unit,其它情况必须显式声明类型
  • 如果时递归函数,必须显式声明类型

二、val和var

var:代表变量

val:代表常量,即通过val定义的变量具有Java中final关键字的效果,也就是引用不可变

val 示例:

val x = intArrayOf(1,2,3)
x = intArrayOf(2,3,4)   //报错,val声明,引用不可变
//虽然x引用不可变,但是它的值可以改变
x[0] = 2

小结:val声明的变量是只读变量,它的引用不可更改,但并不代表其引用对象也不可更改。

三、高阶函数和Lambda

1、函数作为参数

实例:Shaw因为旅游喜欢上了地理,作为一名程序员,他设计了一个App用来对国家数据进行操作。Shaw偏好欧洲的国家,于是它就设计了一个程序来获取欧洲的所有国家。

data class Country(   //数据类,小括号括起来
    val name: String,
    val continiet: String,
    val population: Int)


class CountryAPP {
    fun filterCountries(countries: List<Country>): List<Country> {
        val res = mutableListOf<Country>()
        for (c in countries) {
            if (c.continiet == "EU") {  //获取欧洲国家
                res.add(c)
            }
        }
        return res
    }
}

后来,Shaw对非洲也产生了兴趣,于是又改进了原来的设计,支持通过集体的州来筛选国家

   fun filterCountries(countries: List<Country>, continiet: String): List<Country> {
        val res = mutableListOf<Country>()
        for (c in countries) {
            if(c.continiet == continiet) {  //通过continiet来筛选州
                res.add(c)
            }
        }
        return res
    }

然而,Shaw的地理知识越来越丰富了,他想对国家的特点做一些研究,比如筛选具有一定人口规模的国家,因此他又将程序重新设计了

    fun filterCountries(countries: List<Country>, continiet: String, population: Int): List<Country> {
        val res = mutableListOf<Country>()
        for(c in countries) { 
            if (c.population == population && c.continiet == continiet) {     //通过popualtion和continiet来筛选州
                res.add(c)
            }
        }
        return res
    }

如果按照现有的设计继续开发,更多的筛选条件会作为方法的参数,然后业务逻辑会更难理清。这时,Shaw了解了Kotlin是支持高阶函数的,理论上是可以将筛选的逻辑当坐参数传入的,于是他立马写了一个测试类

class CountryTest{
    fun isBigEuropeanCountry(country:Country): Boolean {
        return country.continiet == "EU" && country.population > 10000
    }
}

调用isBigEuropeanCountry()方法就可以实现筛选功能,然而要怎么样才能将这个方法变成filterCountries 的一个参数呢?

我们先了解一下Kotlin中函数的类型:

栗子:

(Int) -> Unit   //函数的参数是Int类型,返回值是Unit类似于Java中的Void

注意:

  • 通过->来组织参数类型和返回值类型,左边是参数类型,右边是返回值类型
  • 必须用一个括号来包裹参数类型
  • 返回值类型即使是Unit,也必须显式声明

举例:

() -> Unit   //没有参数的函数类型
(Int, String) -> Unit   //多个参数用,间隔
(code: Int,message: String) -> Unit    //为参数指定名称
(code: Int,message: String?) -> Unit   // ? 表示该类型可空(null) 
(Int) -> ((Int) -> Unit)   //支持返回类型也是函数,可以简化为  (Int) -> Int -> Unit

在学习了Kotlin的函数类型后,Shaw重新定义了filterCountries 方法的参数声明

 fun filterCountries(countries: List<Country>,test: (Country) -> Boolean): List<Country> {  
     //test的类型是一个函数,该函数的参数为Country,返回值为Boolean类型
        val res = mutableListOf<Country>()
        for (c in countries) {
            if (test(c)) {  //直接调用test来筛选
                res.add(c)
            }
        }
        return res
    }
2、匿名函数

Shaw发现每增加一个需求都需要专门写一个筛选方法,这样不太好,于是决定使用匿名函数来优化。

countryApp.filterCountries(countries,fun(country: Country): Boolean {
    return country.continient == "EU" && country.population > 10000
})
3、Lamdba表达式

采用匿名函数优化之后确实比原来更好,省去了每次都要创建筛选方法的步骤。然而Shaw发现,编译器会推到类型,也就是说匿名函数中的fun(country: Country)是没有必要的,而且return关键字也可以省略。如此,Shaw又对它的程序进行了改动。

countryApp.filterCountries(countries,{
    country ->
    country.continient == "EU" && country.population > 10000
})

Lambda的语法:

val sum: (Int,Int) -> Int = (x: Int,y: Int -> x + y)
//由于支持类型推导,可以简化为
val sum = {x: Int,y: Int -> x + y} 
或者
val sum: (Int,Int) -> Int = {x,y -> x + y}

总结

  • 一个Lambda表达式必须通过{}包裹

  • 如果Lambda声明了参数部分的类型,且返回值类型支持类型推导,那么Lambda变量就可以省略类型声明、

  • 如果Lambda表达式返回的不是Unit,那么默认最后一行表达式的值类型就是返回值类型,如

    val foo = {x: Int -> 
    	val y = x + 1
    	y  //返回值
    }
    

四、表达式编程

表达式可以是一个值、常量、变量、操作符、函数,或他们之间的组合,编程语言对其进行解释和计算,以求产生另一个值。

//通俗的讲,表达式就是可以返回值得语句
1  //单纯的字面量表达式,值为1
-1 //增加前缀操作符,值为-1
1 + 1 //加法操作符,返回2
listOf(1,2,3)  //列表表达式
"kotlin".length()  //值为6
{x: Int -> x + 1}  //Lambda表达式,类型为(Int) -> Int
fun(x: Int) {println(x)}   //匿名函数表达式,类型为(Int) -> Unit
if(x > 1) x else 1   //if-else表达式,类型为Int

Kotlin中的表达式都是有返回值类型的,即使返回值为空,也是一种类型,即Unit类型

Unit类型:之所以不能说Java中的函数调用皆是表达式,是因为存在特例void。Java中如果函数没有返回值,那么他就需要用void来修饰。由于函数没有值和类型信息,所以就不能算作一个表达式。而Kotlin引入了Unit,Unit其实与Int一样,都是一种类型,然而它不代表任何信息,用面向对象的术语来表示就是一个单例,它的实例只有一个,可写为()。

复合表达式:由于Kotlin中的每个表达式都有值,因此更容易进行组合构成一个复合表达式。

val res: Int? = try{  //try表达式,返回值类型由try和catch部分决定,finally不受影响
    if(result.sucess) {  //if-else表达式
        jsonDecode(result.response)
    }else null
}catch(e: JsonDecodeException) {  
    null
}
//这段程序表示获取一个HTTP响应结果,对其进行json解码,最终赋值给res的过程

Kotlin中的?: 在Kotlin中?用来表示一种类型的可空性,可以使用?:来指定类型为空时的值

val res: Int? = null   //类型可空
println(res ?: 0) //res为空时返回0
1、枚举类

示例:

enum calss Day{
    MON,
    TUE,
    WEN,
    THU,
    FRI,
    SAT,
    SUN
}

枚举类可以拥有构造参数,以及定义额外的属性和方法

enum class DayWeek(val day: Int) {
    MON(1),
    TUE(2),
    WEN(3),
    THU(4),
    FRI(5),
    SAT(6),
    SUN(7);  //如果定义了额外的属性或方法,必须用;分割

    fun getDayNumber(): Int{
        return day
    }
}
2、when表达式

Shaw给他的一天做了计划,如下:

  • 周六打篮球
  • 周日钓鱼
  • 周五晚上约会
  • 平常如果天晴就去图书馆看书,不然就在寝室学习
   fun schedule(sunny: Boolean,day: Day) = when(day) {
        Day.SAT -> basketball()
        Day.SUN -> fishing()
        Day.FRI -> appointment()
        else -> when{
            sunny -> library()
            else -> study()
        }
    }

when表达式的返回类型是所有分支相同的返回类型,或公共的父类型。

当分支 -> 左边的部分返回Boolean时,when表达式可以省略()

when{
    sunny -> library()
    else -> study()
}
3、for循环和范围表达式

示例

for(i in 1..10) println(i)
//等价于
for(i in 1..10) {
    println(i)
}

..在Kotin中是范围表达式,..操作符是按升序排列的闭区间范围[i,j],也就是说该类型必须实现java.lang.Comparable接口

//String类实现了Comparable接口,因此可以使用..来表示区间范围
//字符串的大小根据首字母在字母表中的排序进行比较,如果首字母相同则从左到右获取下一个字母
"abc".."XYZ"

另外,对整数for循环时可以定义迭代的步长

for(i in 1..10 step 2) println(i)
//for循环还支持倒序
for(i in 10 downTo 1 step 2) println(i)  //使用downTo来表示倒叙

Kotlin还有一个函数unitl来实现一个半开区间

for(i in 1 until 10) println(i)
//123456789  [1,10)

in关键字:用来检查一个元素是不是一个区间或者集合中的成员

"a" in listOf("b","c")  //false
"a" !in listOf("b","c")  //true  加上!就是相反的结果
//可以结合..范围表达式来表示更多含义
"kot" in "abc".."XYZ"   //等价于   "kot" >= "abc" && "kot" <= "XYZ"

此外,还可以用withIndex方法,提供一个键值元组

for((index,value) in array.withIndex()) {
    println("the element at $index is $value")  
}
4、中缀表达式

以上的in、step、downTo、until,他们可以不通过点号而是通过中缀表达式的方法被调用,从而让语法变得更加简洁,中缀表达式形如A 中缀方法 B

中缀函数的定义

  • 中缀函数必须是某个类型的扩展函数或者成员方法

  • 中缀函数只能有一个参数

  • 虽然Kotlin的函数参数支持默认值,但是中缀函数的参数不能有默认值

  • 该参数不能是可变参数

    Kotlin中通过vararg关键字来定义可变参数,类似于java中的… java中的可变参数必须是最后一个参数,而Kotlin中没有限制

     fun printLetters(vararg letters: String, count: Int): Unit {
            print("$count letters are")
            for (letter in letters) print(letter)
        }
    
    //使用方法
    //一、直接传入字符串数组
    printLetters("a","b","c",count = 3)
    //二、使用*传入已有的变量
    val letters = arrayOf("a","b","c")
    printLetters(*letters,count = 3)
    

to函数:to函数也是一个中缀表达式,它的返回值是Pair的键值对结构,因此经常和map一起使用

mapOf{
     1 to "one"
     2 to "two"
     3 to "three"
}

自定义中缀表达式

class Person{
    infix fun called(name: String) {
        println("my nam is ${name}")
    }
}

fun main(args: Array<String>) {
    val p = Person()
    p called "Shaw"
}
//my name is Shaw
//Kotlin仍然支持使用普通方法的语法调用中缀函数,如
p.called("Shaw")

五、字符串定义及操作

示例

val str = "hello world!"
str.length  //12
str.subString(0,5)  //hello
str + " hello korlin!"  //hello world! hello kotlin!
str.replace("world","kotlin")  //hello kotlin!
//由于String是一个字符序列,因此可以遍历
for(i in str.toUpperCase()) {print(i)}
//还可以访问字符序列的成员
str[0] //h
str.first() //h
str.last() //!
str[str.length - 1] //!
1、原生字符串

示例

val htmlText =""""
            <html>
                <body>
                    <p>hello kotlin!</p>
                </body>
            </html>
       """

上面演示的一段代码就是原生字符串,在Kotlin中使用"""来定义原生字符串。用这种三个引号定义的字符串,最终的打印结果与代码中呈现的格式一致,而不会解释转义字符如\n,以及Unicode的转义字符如\uXXXXX

2、字符串模板

示例

//拼接字符串
fun message(name: String, lang: String) = "Hi ${name},welcome to ${lang}"

Kotlin的字符串模板支持将变量植入字符串,使用${name}的格式来操作。除此之外,我们还可以将表达式植入字符串

"kotlin has ${if("kotlin".length > 0) "kotlin".length else "no"} letters"
3、字符串判等

Kotlin中的判等有两种类型:

结构相等:通过==来判断两个对象的内容是否相等

引用相等:通过===来判断两个对象的引用是否一样,相反的操作是!==

示例

var a = "Java"
var b = "Java"
var c = "Kotlin"
var d = "Kot"
var e = "lin"
var f = d + e

a == b  //true
a === b //true
c == f //true
c === f //false
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值