Kotlin基础语法
Kotlin基础语法文章目录Kotlin基础语法1.包2.声明变量和值3.变量类型推断4.字符串与其模板表达式5.流程控制语句6.代码注释7.语法与标识符1.包比如说 程序员A写了一个类叫JSON,程序员B也写了一个类叫JSON,然后我们写代码的时候想要同时使用这两个类,该怎么区分呢?一个答案是使用目录命名空间。对应在java中就是使用包(package)来组织类,以确保类名的唯一性。上面说的例子
Kotlin基础语法
文章目录
1.包
比如说 程序员A写了一个类叫JSON,程序员B也写了一个类叫JSON,然后我们写代码的时候想要同时使用这两个类,该怎么区分呢?一个答案是使用目录命名空间。对应在java中就是使用包(package)来组织类,以确保类名的唯一性。上面说的例子,A写的类放到package com.abc.fastjson中 ,B写的类就放到package com.bbc.jackjson中。这样我们在代码中,就可以根据命名空间来分别使用这两个类。调用如下:
com.abc.fastjson.JSON.toJSONString();
com.bbc.jackjson.JSON.parseJSONObject();
kotlin也沿袭了Java的package概念,同时做了些扩展
我们可以在*.kt文件开头声明package命名空间。例如在PackageDemo.kt源代码中,我们按照如下方式声明包:
package com.easy.kotlin
fun what(){
println("This is WHAT ?")
}
class Motorbike{
fun drive(){
println("Drive the Motorbike ...")
}
}
fun main(args:Array<String>){
println("Hello,World!")
}
包的声明处于源文件顶部。这里,我们声明了包com.easy.kotlin,里面定义了包级函数what(),同时定义了一个类Motorbike。另外,目录与包的结构 无需匹配:源代码可以在文件系统的任意位置。
2.声明变量和值
首先,在Kotlin中,一切都是对象,所以,所有变量也都是对象(也就是说,任何变量都是根据引用类型来使用的)。
Kotlin的变量分为var(可变的) 和 val(不可变的)。可以简单的理解为:
var是可写的,在它生命周期中可以被多次赋值
val是只读的,仅能一次赋值,后面就不能被重新赋值
3.变量类型推断
- 省去变量类型 在Kotlin中,大部分情况下不需要说明你使用对象的类型,编译器可以直接推断出它的类型代码如下:
fun typeInference(){
val str = "abc"
println(str) //输出abc
println(str is String) //true
println(str::class) //class java.lang.String(Kotlin reflection is not available)
println(str::class.java) //class java.lang.String
}
所以,我们只需要依据要产生的变量类型填写var或val,其类型通常能够被推断出来,编译器能够检测其类型,自动完成类型转换。当然,我们也可以明确地指定变量类型。但是类型推断不是所有的。例如,整形变量Int不能赋值Long变量。下面的代码不能通过编译
fun Int2Long(){
val x:Int = 10
val y:Long = x //Type mismatch
val y: Long = x.toLong()
}
- 使用is运算符进行类型检测 is运算符检测一个表达式是否为某类型的一个实例。如果一个不可变的局部变量或属性已经判断为某类型,那么检测厚的分支中可以直接当作该类型使用,无需显式转换:
fun getLength(obj: Any): Int? {
var result = 0
if(obj is String){
//'obj'在该条件分支自动转换成‘String'
println(obj::class) // class java.lang.String
result = obj.length
println(result)
}
//在离开类型检测分支后,‘obj'仍然是‘Any'类型
println(obj::class) //class java.lang.Object
return result
}
4.字符串与其模板表达式
原始字符串(raw string)由三重引号(""")分隔(这个跟python一样)。原始字符串可以包含换行符和任何其他字符
package com.easy.kotlin
fun main(args: Array<String>){
val rawString = """
fun helloWorld(val name : String){
println("Hello,world!")
}
"""
println(rawString)
}
字符串可以包含模板表达式。模板表达式以美元符号($)开始:
val fooTemplateString = "$rawString has ${rawString.length} characters"
println(fooTemplateString)
5.流程控制语句
流程控制语句是编程中的核心之一,一般可以分为如下三类
- 分支语句(if,when) if-else语句是控制程序流程的最基本形式,其中 else可选。在Kotlin中,if是一个表达式,即它会返回一个值
代码示例如下:
package com.easy.kotlin
fun main(args: Array<String>){
println(max(1,2))
}
fun max(a: Int,b: Int): Int{
//作为表达式
val max = if (a > b) a else b
return max // return if ( a > b) a else b
}
fun max1 (a: Int,b:Int): Int{
//传统用法
var max1 = a
if( a < b) max1 = b
retrun max1
}
fun max2(a: Int,b: Int) : Int{
//With else
var max2: Int
if(a > b){
max2 = a
} else {
max2 =b
}
return max2
}
另外,if的分支可以是代码块,最后的表达式作为该块的值
fun max3(a: Int,b: Int): Int{
val max = if(a > b){
print("Max is a")
a
} else {
print("Max is b")
b
}
return max
}
if作为代码块时,最后一行为其返回值
另外,在Kotlin中没有类似true?1:0这样的三元表达式。对应的写法是使用if else 语句:
if(true) 1 else 0
如果if表达式只有一个分支,或者分支的结果是Unit,它的值就是Unit
示例如下:
>>> val x = if(1==1) true
>>> x
kotlin.Unit
>>> val y = if(1==1) true else false
>>> y
true
if-else 语句规则
1)if后的括号不能省略,括号里的表达式的值必须是布尔型
2)如果条件体内只有一条语句需要执行,那么if后面的大括号可以省略。良好的编程风格建议加上大括号
3)对于给定的if,else语句是可选的,else if语句也是可选的。else 和 else if 同时出现时,else 必须出现在 else if之后
4)else 和 else if 同时出现时,else 必须出现在 else if之后
5)如果有多条else if语句同时出现,那么如果有一条else if语句的表达式测试成功,那么会忽略掉其他所有else if和else的分支
6)如果出现多个if ,只有一个else 的情形,else子句归属于最内层的if语句。这些规则跟Java ,C语言基本相同。
- when表达式 when表达式类似于 switch-case表达式。 when会对所有的分支进行检查,直到有一个条件满足为止。但相比switch-case而言,when的语句更加强大,灵活。Kotlin的极简语法表达风格,使得我们对分支检查的代码写起来更加简单直接:
fun cases (obj: Any){
when(obj){
1->print("第一项")
"hello" - >print("这个是字符串hello")
is Long->print("这是一个Long类型数据")
! is String->print("这不是String类型的数据“)
else -> print("else 类似于Java中的default")
}
}
像if一样,when的每一个分支也可以是一个代码块,它的值是块中最后的表达式的值。如果其他分支都不满足条件,则会到else分支(类似default)。如果我们有很多分支需要用相同的方式处理,则可以把多个条件分支条件放在一起,用逗号分隔:
fun switch(x: Any){
when(x){
-1,0 -> print("x == -1 or x == 0 ")
1 -> print("x == 1")
2 -> print("x == 2")
else -> {
print("x is neither 1 nor 2")
}
}
}
可以用任意表达式(而不只是常量)作为分支条件
fun switch(x: Int){
val s = "123"
when(x){
-1,0 -> print("x == 1 or x ==0")
1 -> print("x == 1")
2 -> print("x == 2")
8 -> print("x is 8")
parseInt(s) -> println("x is 123")
else -> {
print("x is neither 1 nor 2")
}
}
}
也可以检测一个值在(in)或者不在(!in)一个区间或者集合中:
val x = 1
val validNumbers = arrayOf(1,2,3)
when(x){
in 1..10 -> print("x is in the range")
in vaildNumbers -> print("x is valid")
!in 10..20 ->print("x is outside the range")
else -> {
print("none of the above")
}
}
-
循环语句(for,while)Kotlin的for循环与现代的程序设计语言基本相同,这里就不做过多介绍了
-
break和continue 与现代的程序设计语言基本相同,这里就不做过多介绍了
-
return 在Kotlin中,除了表达式的值,返回值的函数都要求显示地使用return来返回其值
fun sum(a: Int,b: Int): Int{
return a + b
}
fun max(a: Int,b: Int): Int { if (a > b) return a else return b}
在Kotlin中,可以直接使用=符号来直接返回一个函数的值
代码示例如下:
>>> fun sum(a: Int,b: Int) = a + b
>>> fun max(a: Int,b: Int) = if (a > b) a else b
>>> sum(1,10)
11
>>> max(1,2)
2
>>> val sum =fun(a:Int,b:Int) = a+b
>>> sum
(kotlin.Int,kotlin.Int) -> kotlin.Int
>>> sum(1,1)
2
>>> val sumf = fun(a:Int,b:Int) = {a+b}
>>> sumf
(kotlin.Int,kotlin.Int) -> () -> kotlin.Int
>>> sumf(1,1)
() -> kotlin.Int
>>> sumf(1,1).invoke()
2
上述代码示例中,我们可以看到,后面的函数体语句有没有大括号{}意思完全不同。加了大括号,意义就完全不一样了,通过下面的代码示例可以清晰的看出:
>>> fun sumf(a:Int,b:Int) = {a+b}
>>> sumf(1,1)
()->kotlin.Int
>>> sumf(1,1).invoke
error: function invocation 'invoke()' expected
sumf(1,1).invoke
>>> sumf(1,1).invode()
2
>>> fun maxf(a:Int,b:Int) = {if(a>b) a else b}
>>> maxf(1,2)
() -> kotlin.Int
>>> maxf(1,2).invoke()
2
可以看出,sumf,max的返回值是函数类型
() -> kotlin.Int
() -> kotlin.Int
这点跟Scala是不同的。在Scala中,带不带大括号{}意思都一样
Kotlin中return语句 会从最近的函数或匿名函数中返回,但是在Lambda表达式中遇到return ,则直接返回最近的外层函数,例如如下面两个函数是不同的:
fun returnDemo_1(){
println(" START " + ::returnDemo_1.name)
val intArray = intArrayOf(1,2,3,4,5)
intArray.forEach(){
if(it == 3) return
println(it)
}
println(" END " + ::returnDemo_1.name)
}
//1
//2
fun returnDemo_2(){
println(" START " + ::returnDemo_2.name)
val intArray = intArrayOf(1,2,3,4,5)
intArray.forEach(fun(a: Int) {
if(a == 3) return
println(a)
})
println(" END " + ::returnDemo_2.name)
}
//1
//2
//4
//5
returnDemo_1在遇到3时会直接返回(有点类似循环体中的break行为)。最后输出为: 1 , 2
returnDemo_2在遇到3时会跳过它继续执行(有点类似循环体中的continue行为)。最后输出为:1 ,2 ,4 ,5
在returnDemo_2中,我们用一个匿名函数替代lambda表达式。匿名函数内部的return语句将从该匿名函数自身返回
在Kotlin中这是匿名函数和lambda表达式行为不一致的地方。当然,为了显示地指明return返回的地址,为此Kotlin还提供了@Label(标签)来控制返回语句,且看下节分解。
- 标签 在Kotlin中任何表达式都可以用标签(label)来标记。标签的格式为标识符后跟@符合,例如abc@,jarOfLove@都是有效的标签。我们可以用Label标签来控制return ,break 或continue的跳转(jump)行为。
Kotlin的函数是可以被嵌套的。它有函数字面量,局部函数等。有了标签限制的return,我们就可以从外层函数返回了。例如,从lambda表达式中返回,returnDemo_2()我们可以显示地制定lambda表达式中的return地址是其入口处。
代码示例如下:
fun returnDemo_3(){
println(" START " + ::returnDemo_3.name)
val intArray = intArrayOf(1,2,3,4,5)
intArray.forEach here@{
if(it == 3) return @here//指令跳转到 lambda表达式标签here@ 处。
println(it)
}
println(" END " + ::returnDemo_3.name)
}
//1
//2
//4
//5
我们在lambda表达式开头处添加了标签here@,可以这么理解:该标签相当于是记录了lambda表达式的指令执行入口地址,然后在表达式内部使用return@here跳转至lambda表达式该地址处。另外,也可以使用隐式标签更方便,该标签与接收该lambda的函数同名。
代码示例如下:
fun returnDemo_4(){
println(" START " + ::returnDemo_4.name)
val intArray = intArrayOf(1,2,3,4,5)
intArray.forEach {
if(it == 3){
return@forEach //从lambda表达式@forEach中返回。
println(it)
}
}
println(" END " + ::returnDemo_4.name)
}
- throw表达式
在Kotlin中throw是表达式,它的类型是特殊类型:Nothing,该类型没有值。与C,Java中的void意思一样:
>>> Nothing::class
class java.lang.Void
我们在代码中,用Nothing来标记无返回的函数:
>>> fun fail(msg:String):Nothing{ throw IllegalArgumentException(msg)}
>>> fail("XXXX")
java.lang.IllegalArgumentException: XXXX
at Line57.fail(Unknown Source)
另外,如果把一个throw表达式的值赋值给一个变量,需要显示声明类型为Nothing:
>>> val ex = throw Exception("YYYYYYYY")
error: 'Nothing' property type needs to be specified explicity
val ex = throw Exception("YYYYYYYY")
>>> val ex:Nothing = throw Exception("YYYYYYYY")
java.lang.Exception: YYYYYYYY
另外,因为ex变量是Nothing类型,没有任何值,所以无法当作参数传给函数
6.代码注释
正如Java和JavaScript ,Kotlin支持行注释及块注释
//这是一个行注释
/× 这是一个多行的
块注释 ×/
与Java不同的是,Kotlin的块注释可以嵌套。就是说,你可以这样注释:
7.语法与标识符
我们知道,任何一门语言都会有一些自己专用的关键字,符号以及规定的语法规则等等。程序员们使用这些基础词汇和语法规则来表达算法步骤,也就是写代码的过程。
词法分析是编译器对源码进行编译的基础步骤之一。词发分析是将源程序读入的字符序列,按照一定的规则转换成词法单元(Token)序列的过程。词法单元是语言中具有独立意义的最小单元,包括修饰符,关键字,常数,操作符,等等
.修饰符
在kotlin源码项目中 kotlin/grammar/src/modifiers.grm文件中,描述了Kotlin语言的修饰符:modifiers:(modifier | annotations) * ; typeModifiers:(suspendModifier | annotations)*;
modifier : classModifier
:accessModifier
:varianceAnnotation
:memberModifier
:parameterModifier
:typeParameterModifier
:functionModifier
:propertyModifier
classModifier 类修饰符
: "abstract"抽象类
: “final” 不可被继承final类
: "enum"枚举类
: "open"可继承open类
: "annotation"注解类
: "sealed"密封类
: "data"数据类
memberModifier
: "override"重写函数
: "open"可被重写
: "final"不可被重写
: "abstract"抽象函数
: "lateinit"后期初始化
accessModifier 访问控制权限 默认public
: “private”
: “protected”
: “public”
: “internal”
其中,internel访问权限,指的是整个模块内可访问。模块(module)是指一起编译的一组Kotlin源码代码文件:例如,InteliJ IDEA模块,Maven项目 或者Gradle项目,通过Ant任务的一次调用编译的一组文件等。
.关键字
每一门语言都有自己的关键字集合,在Kotlin中,这些关键字定义如下:
this 关键字的使用场景 1.在类的成员中,this指向的是该类的当前对象 2.在扩展函数或者带接收者的函数字面值中,this表示在点左侧传递的接收者参数 3.如果this没有限定符,它指的是最内层的包含它的作用域。
super关键字 持有指向其父类的引用
.操作符
这里重点介绍Elvis操作符? 在Kotlin中 Elvis操作符特定是跟null比较,主要用来做null安全性检查 也就是说 y =x?:0 等价于 val y = if(x !== null) x else 0
Elvis 操作符? 是一个二元运算符,如果第一个操作数为真,则返回第一个操作数,否则将计算并返回其第二个操作数。它是三元条件运算符的变体。
.扩展函数和扩展属性
Kotlin支持扩展函数和扩展属性。能够扩展一个类的新功能而无需继承该类或使用像装饰者这样的设计模式等
.空指针安全
Java最大的一个问题是 null。 这个问题Kotlin针对Java做了改进。它使用了显式的null,这会强制我们在必要时进行null检查。Kotlin通过类型系统中的可空类型来实现空指针的安全检查。关于Kotlin的可空类型以及类型系统将在下章介绍。
.标准库API简介
具体参考:http://kotlinlang.org/api/latest/jvm/stdlib/index.html
更多推荐



所有评论(0)