Kotlin语法简记
一、基础语法
包声明
Kotlin语言的源文件,不需要对应匹配文件目录和包。
默认包名
default
,都会默认导入kotlin
下的相关必须包//包名中如果含有,kotlin 的关键字,则需要用 反引号 ` 来包装一下 package `in`.zhiwei.aqi
函数定义
fun
关键字,格式为args:type
,返回类型// fun sum(a:Int,b:Int):Int{ return a+b }
若使用
lambad
类的表达式,返回类型会自动推断。但:public
的函数,必须注明返回类型fun sum(a:Int,b:Int) = a+b //此时会自动推断返回类型 public fun sum(a:Int,b:Int):Int = a+b //public函数,必须注明返回类型
Unit
无返回类型,类似于Java中的Void
fun printSum(a:Int,b:Int):Unit{ print(a+b) } //Unit类型,可以省略,public函数,也可以。
vararg
可变参数fun vars(vararg v:Int){ for(vt in v){ print(vt) } } //调用vars函数 fun main(args:Array<String>){ vars(1,2,3,4,5) }
lambda
匿名函数fun main(args:Array<String>){ val sumLambda:(Int,Int)-> Int = {x,y -> x+y} println(sumLambda(1,2)) }
常量、变量
定义变量,常量,使用
var
,val
关键字,格式var/val name : Type = value
,其中Type可以省略,自动推算fun main(args:Array<String>){ var abc:Int = 3 val PI = 3.1415926 println(PI) println(abc) }
注释
kotlin注释,单行
//
,多行/* text */
且可以嵌套。字符串模板
$
符号$
表示变量名、或变量值$varName
表示变量值${varName.fun()}
表示返回值
var a = 1 //变量值 val s1 = "a is $a" a = 2 //返回值 val s2 = "${s1.replace("is","was")},but now is $a"
NULL检查机制
Kotlin的NULL检查,两种:
!!
符号,抛出异常?
符号,不处理,返回值为null
。或者?:
三目运算
//当引用可能为null时候,类型声明必须标记可为null //下面示例,str内容若非整数,则会null,所以返回类型必须声明可为null fun parseInt(str:String):Int?{ //.... }
类型转换
使用
is
、!is
fun getStringLenght(obj:Any):Int?{ if(obj is String){ //类型判断后,会自动转换 return obj.length } // !is表示,不是某个类型 return null }
区间
区间表达式,
..
的rangeTo,配合in
或!in
。注意范围,数学中的左右闭区间[1,4]
for (i in 1..4) print(i) // output "1234" for (i in 4..1) print(i) // output nothing //指定步长 for (i in 1..4 step 2) print(i)// output "13" for (i in 4 downTo 1 step 2) print(i) // output "42" //使用until函数,排除结束的那个元素 for(i in 1 until 10){//[1,10) print(i) }
二、基础数据类型
类型 | 位宽度 |
---|---|
Double | 64 |
Float | 32 |
Long | 64 |
Int | 32 |
Short | 16 |
Byte | 8 |
- 字面常量
- 十进制:123
- 长整形:123L
- 16进制:0x0F
- 2进制:0b00001011
- 注:不支持八进制
- Doubles:123.5,123.5e10
- Floats:123.5f 或123.5F
kotlin
//可以使用下划线,分隔数字,便宜阅读
val oneMillion = 1_000_000
val hexBytes = 0xFF_EC_DE_5E
数字比较
在上面可以看出,Kotlin都是Double、Int、Float等封装后的类型,没有基础的数据类型,避免空指针。
===
对比地址==
对比值
for main(args:Array<String>){ val a = 100 print(a===a)//true,自身对比,值与地址皆同 //包装 val boxA:Int? = a val boxB:Int? = a //对比 print(boxA == boxB)//true 值相等 print(boxA === boxB)//false 对象不同,地址不同 }
类型转换
不同于Java的小到大可以自动转换,Kotlin不可以。
val b:Byte = 1 val i:Int = b //这就错了,b是Byte,不能转化为Int,他们都是包装后的 val l = 1L + 3 //Int和Long这类还可以自动转 //其他可以使用toByte,toInt等
位操作符
适用于Int和Long
shl(bits) - 左位移(Java中的<<) shr(bits) - 右位移(Java中的>>) ushr(bits) - 无符号右位移(Java中的>>>) and(bits) - & or(bits) - | xor(bits) - ^ inv() - reverse
字符
Kotlin中Char不可与数字直接操作,必须
'char'
单引号包裹c == '1'//不能写作 c == 1 //特殊字符可以转义,或者Unicode表示
Boolean
||、&&、!
Array
//[1,2,3] val arrA = arrayOf(1,2,3) val arrB = Array(3,{i -> (i*2)})
Kotlin中,数组是不变的类型
字符串String
- 可以
"""
三对双引号,来多行 - for遍历某个字符
- trimMargin()来删除空白
val text = """ |第一行 |第二行 |第三行 """ //默认使用`|`作为边界前缀,也可以自定义 trimMargin(">")
- 可以
三、条件控制
if表达式
var max = a if(a<b){ max = b }else{ max = a } //或者,类似Java中的三目运算符 var max = if(a<b) a else b
使用区间
//x..y var a = 7 if(x in 1..9) print("a in this range")
when
类似其他编程语言中的
Swith...case
when(x){ 1-> print("x == 1") 2-> print("x == 2") 3,4-> print("x == 3 or 4") in 5..9-> print("x is in this range")//还有!in,is ,!is in validNumbers-> print("x is valid") else-> print("x not 1 or 2")//类似defalut }
四、循环控制
for循环
for(item in collection) print(item) for(i in array.indices) print(array[i]) for((index,value) in array.withIndex()) { print("item $index is $value") }
while,do…while
return、break、continue
//Break和Continue 可以配合label标签,确定其作用范围,格式label@ loop@ for(i in 1..100){ for(j in 1..100){ if(...) break@loop } } //return 返回,也可以用label标签确定范围 fun foo(){ ints.forEach{ if(it == 0)return//这么return,就return了真个foo函数 print(it) } } //配合lable fun foo(){ ints.forEach lit@{ if(it == 0)return@lit//如此,只return出forEach, print(it) } } //可以不用显示声明label, fun foo(){ ints.forEach{ if(it == 0)return@forEach//隐式label的使用 print(it) } } //也可以,将lambad中声明匿名函数,return就会在它的范围内return fun foo(){ ints.forEach(fun(value:Int)){ if(it==0)return print(it) } } //return带有返回值的时候,带有label的return,优先 return@abc 1//从标签abc那里,返回出去数值 1
五、类和对象
//定义类
class Demo{
var name:String = "Joe"
var url:String = "www.zhiwei.in"
val PI = 3.1415926
//成员函数
fun foo(){
print("Foo")
}
}
//空类
class Empty
//实例化
val demo = Demo()//kotlin 中没有new
demo.name
//构造函数,一个主的,可有多个次的,主构造函数,就在class标题处
class Person constructor(firstName:String){
//...
}
//若主构造函数,方法体为空,则constructor可以省略
class Person(firstName:String){}
//geter setter 可选
var lastName:String = "Green"
get()=field.toUpperCase()
set
var no:Int = 100
get() = field
set(value){
...
}
var height:Float = 175.0f
private set
//field关键字,用于后端变量,kotlin中不能有字段
//非空属性必须定义时候就初始化,kotlin使用lateinit来延迟初始化
public class MyTest{
lateinit var subject:TestSubject
@SetUp fun setup(){
subject = TestSubject()
}
@Test fun test(){
subject.method()
}
}
主构造器
不能包含任何代码,需要在
init
中初始化
class Person constructor(firstName:String){
init{
//...
}
}
//类的参数,可以在构造函数中
class People(val firstName:String, val lastName:String){
//...
}
次构造函数
class Person{ //如果没有主构造函数,则每个次构造函数,可能需要依次调用代理 //类似与Java中的多个构造函数,内部依次调用 constructor(parent:Person){ parent.children.add(this) } } //没有显式的声明构造函数的话,会有一个默认的public的构造函数,类似于Java
抽象类
abstract
关键字//可继承 open class Base{ open fun f(){} } abstract class Sub:Base{ override abstract fun f() }
嵌套类
class Outer{ private val bar:Int = 1 class Nested{ fun foo() = 2 } } //调用嵌套 fun main(args:Array<String>){ val demo = Outer.Nested().foo()//调用格式,Outer.Nested().foo() println(demo) }
内部类
inner
关键字//内部类会拥有外部类的引用,不同于嵌套类 class Outer{ private val bar:Int = 1 var v = "member property" inner class Inner{ fun foo() = bar fun innerTest(){ var o = this@Outer println("持有外部引用:"+o.v) } } } //调用 fun main(args:Array<String>){ val demo = Outer().Inner().foo()//格式,与嵌套类,略有不同 println(demo) }
匿名内部类
class Test{ var v = "abc" fun setInterFace(test:TestInterFace){ test.test() } } //interface interface TestInterFace{ fun test() } //调用 fun main(args:Array<String>){ var test = Test() //匿名内部类 test.setInterFace(obj:TestInterFace{ override fun test(){ //... } }) }
类的修饰符
- 属性修饰符
symbol | description |
---|---|
abstract | 抽象 |
final | 不可继承,kotlin的类,默认都是final |
enum | 枚举 |
open | 可继承 |
annotation | 注解类 |
- 权限修饰符
symbol | description |
---|---|
private | 私有,同一文件内可见 |
protect | 同一文件内,或子类 可见 |
public | 公开 |
internal | 同一模块中可见 |
六、继承
kotlin中所有类的继承,都是Any
类,类似于Java中的Object
类
//Any类中含有以下三个函数
equals()
hashCode()
toString()
//open声明可被继承
open class Base(p:Int)
class Sub(p:Int):Base(p:Int)
- 继承的构造函数
class Student:Person{
//子类没有主构造函数,则需要在次构造函数中,super父类的构造函数
constructor(ctx:Context):super(ctx){}
}
//若是子类有主构造函数,基类也应一致,且在子类继承时候,初始化
open class Person(var name:String,var age:Int){}
//此处,:Person被初始化了name,age
class Student(name:String,age:Int):Person(name,age){
//...
}
- 重写函数,属性
基类函数,默认final,需要open的方可重写,重写用override标记
若继承的基类,或接口中,多个基类或接口含有相同函数,则需要复写,且显示super.fun() 调用
属性的重写
open class Foo{ open val x:Int get{...} } class Bar:Foo(){ override val x : Int = ... } //属性的重写,需要兼容,var可以重写val,但是val不能重写var //可以在构造函数中直接声明 重写 interface Foo{ val count:Int } class Bar1(override val count:Int):Foo class Bar2:Foo{ override var count:Int = 0 }
七、接口
类似Java8,interface
关键字,可有默认实现
interface MyInterface{
fun bar()
fun foo(){
println("foo")
}
}
一个类可以实现多个接口
接口的属性,抽象无值,必须复写
interface MyInterface{ var name:String } class MyImpl :MyInterface{ override var name:String = "Kotlin" }
八、扩展
Kotlin
支持对类的静态扩展,不需要继承,或者装饰器模式设计
- 扩展函数
//格式,扩展对象、扩展函数、参数(Nullable)
fun receiverType.functionName(params){
body
}
//示例:
class User(var name:String)
//新增一个print函数给User类
fun User.Print(){
print("name:$name")
}
//调用
fun main(args:Array<String>){
var user = User("kotlin")
user.Print()//扩展新增的函数
}
静态扩展的,扩展到那个类,就是属于那个类/接口,而不会是它的实现/子类。
属性也可被扩展,但不能初始化。
类内部拥有的其他对象,也可以被扩展
class MyClass{ companion object{}//Companion的对象 } fun MyClass.Companion.foo(){ print("扩展了类的拥有的对象") }
扩展作用域
package foo.bar fun Baz.goo(){...} //其他包使用 package com.example.usage //import 导入 import foo.bar.goo fun usage(baz:Baz){ //就能原本不能访问的 baz.goo() }
扩展成员,分发接收者、扩展接收者
class D{ fun duck() print("Dark duck") } class C { fun cook()print("Cook food") //扩展D的函数 fun D.ext(){ duck()//能够调用D的函数 cook()//也能调用C的函数 } //提供调用扩展函数的方法 fun callDext(d:D){ d.ext()//调用扩展函数 } } //示例 fun main(args:Array<String>){ val c:C = C()//c被称为分发接收者 val d:D = D()//d被称为扩展接收者 c.callDext(d) }
九、数据类&密封类
kotlin
可创建一个只包含数据的类,data
关键字
- 数据类主构造函数,至少一个参数
- 参数标识val或var
- 数据类不能abstract、open、sealed或inner
- 不能继承其他基类,可以多接口实现
附带一些函数equals/hashCode;toString,copy
等
复制:
data class User(val name:String, val age:Int) fun main(args:Array<String>){ val jack = User(name="Jack",age = 1) val olderJack = jack.copy(age = 2) print(jack) print(olderJack) } //解构声明 val jane = User("Jane",27) val(name,age) = jane println("$name,$age years of age") //输出结果:Jane,27 years of age
标准数据类
Pair
和Triple
,但是建议自定义数据类使用密封类
sealed
关键字,用于限定类中的值的类型,而不是任意类型密封类可有子类,但必须在其中内嵌
sealed
不能修饰interface、abstract类,会警告,但不会编译错误
sealed class Expr//密封类
data class Const(val number:Double):Expr()//密封类的子类
data class Sum(val e1:Expr,val e2:Expr):Expr()//密封类的子类
object NotANumber:Expr()
fun eval(expr :Expr):Double = when(expr){
is Const -> expr.number
is Sum -> eval(expr.e1)+eval(expr.e2)
NotANumber -> Double.Nan
}
十、泛型
class Box<T>(t:T){
var value = t
}
//示例,实例化时候,需要具体参数类型
val box:Box<Int> = Box<Int>(1)
//或
val box = Box(1)
//函数
fun <T> boxIn(value:T) = Box(value)
val box2 = boxIn<Int>(1)
val box3 = boxIn(1)//自动推测类型
//可推断的类型,可不申明
fun main(args: Array<String>) {
val age = 23
val name = "runoob"
val bool = true
doPrintln(age) // 整型
doPrintln(name) // 字符串
doPrintln(bool) // 布尔型
}
fun <T> doPrintln(content: T) {
//when内做了类型判断
when (content) {
is Int -> println("整型数字为 $content")
is String -> println("字符串转换为大写:${content.toUpperCase()}")
else -> println("T 不是整型,也不是字符串")
}
}
泛型约束,类似于Java中的T extends ABC之类的
fun <T : Comparable<T>> sort(list:List<T>){
//...
}
//多个上限约束,使用where
fun <T> cloneWhenGreater(list:List<T>,threshold:T):List<T> where T:Comparale,Cloneable{
//...
}
型变
kotlin没有通配符,有
declaration-site variance
和type projections
- in 消费,入参,非出参
- out 生产,作为出参,而不能入参(即,返回类型,而非传入类型)
//定义支持协变的类 class Test<out A>(val a:A){ fun foo():A{ return a } } // fun main(args:Array<String>{ var strCo:Test<String> = Test("a") var anyCo:Test<Any> = Test<Any>("b") anyCo = strCo println(anyCo.foo())//输出结果为 “a” }
//定义一个支持逆变的类 class Test<in A>(a:A){ fun foo(a:A){ //... } } // fun main(args:Array<String>){ var strDCo = Test("a") var anyDCo = Test<Any>("b") strDCo = anyDCo }
星号投射
Function<*,Sting>,表示Function<in Nothing,String> Function<Int,*> ,表示Function<Int,out Any?> Function< , > ,表示Function<in Nothing,out Any?>
十一、枚举类
enum class Color{
RED,BLACK,BLUE,GREEN,YEELOW,WHITE
}
//或
enum class Color(val rgb:Int){
RED(0xFF0000),
GREEN(0x00FF00)
}
//匿名内部类形式
enum class ProtocolState{
WAITING{
override fun signal() = TALKING
},
TALKING{
override fun signal()= WAITING
};
abstract fun signal():ProtocolState
}
枚举中,成员用;
分隔
enum class Color{
RED,BLACK,BLUE,GREEN,WHITE
}
//test
fun main(args: Array<String>) {
var color:Color=Color.BLUE
println(Color.values())
println(Color.valueOf("RED"))
println(color.name)
println(color.ordinal)
}
十二、对象表达式&声明
//匿名类对象
window.addMouseListener(obj:MouseAdapter(){
override fun mouseClicked(e:MouseEvent){
...
}
})
//或继承基类,接口的对象
val ab:A = obj:A(1),B{//A 是基类,B是接口,ab声明一个A类型对象,= 一个A的子类且实现了B接口的对象
override val y = 15
}
//越过类的定义
fun main(args: Array<String>) {
val site = object {
var name: String = "菜鸟教程"
var url: String = "www.runoob.com"
}
println(site.name)
println(site.url)
}
//构造函数,共有的话,则public,
class C {
// 私有函数,所以其返回类型是匿名对象类型
private fun foo() = object {
val x: String = "x"
}
// 公有函数,所以其返回类型是 Any
fun publicFoo() = object {
val x: String = "x"
}
fun bar() {
val x1 = foo().x // 没问题
val x2 = publicFoo().x // 错误:未能解析的引用“x”
}
}
object
关键字,声明对象
object Site {
var url:String = ""
val name: String = "Google Cloud"
}
fun main(args: Array<String>) {
var s1 = Site
var s2 = Site
s1.url = "www.google.com"
println(s1.url)
println(s2.url)
}
//两个输出,都是同一个url
class Site {
var name = "google cloud"
object DeskTop{
var url = "www.google.com"
fun showName(){
print{"desk legs $name"} // 错误,不能访问到外部类的方法和变量
}
}
}
fun main(args: Array<String>) {
var site = Site()
site.DeskTop.url // 错误,不能通过外部类的实例访问到该对象
Site.DeskTop.url // 正确
}
类内部的对象声明,可以用companion
关键字标记,构成伴生,一个类只有一个伴生对象,即companion
只能出现一次
class MyClass{
companion object Factory{
fun create():MyClas = MyClass()
}
}
//access
val instance = MyClass.create()
十三、委托
kotlin直接支持委托模式,即一个类中的方法,调用另一个类的函数实现by
关键字
- 类委托
// 创建接口
interface Base {
fun print()
}
// 实现此接口的被委托的类
class BaseImpl(val x: Int) : Base {
override fun print() { print(x) }
}
// 通过关键字 by 建立委托类,由Base的实现类,具体操作
class Derived(b: Base) : Base by b
fun main(args: Array<String>) {
val b = BaseImpl(10)
Derived(b).print() // 输出 10
}
属性委托
某个属性,是由其他类代理实现
var/val name:Type by 表达式
- 定义为拖类,包含getValue、setValue函数,thisRef、prop
// 定义包含属性委托的类 class Example { var p: String by Delegate() } // 委托的类 class Delegate { operator fun getValue(thisRef: Any?, property: KProperty<*>): String { return "$thisRef, 这里委托了 ${property.name} 属性" } operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) { println("$thisRef 的 ${property.name} 属性赋值为 $value") } } fun main(args: Array<String>) { val e = Example() println(e.p) // 访问该属性,调用 getValue() 函数 e.p = "google kotlin" // 调用 setValue() 函数 println(e.p) }
标准委托
- 延迟属性Lazy
val lazyValue: String by lazy { println("computed!") // 第一次调用输出,第二次调用不执行 "Hello" } fun main(args: Array<String>) { println(lazyValue) // 第一次执行,执行两次输出表达式 println(lazyValue) // 第二次执行,只输出返回值 }
- Observable
class User { var name: String by Delegates.observable("初始值") { prop, old, new -> println("旧值:$old -> 新值:$new") } } fun main(args: Array<String>) { val user = User() user.name = "第一次赋值" user.name = "第二次赋值" } //执行结果 旧值:初始值 -> 新值:第一次赋值 旧值:第一次赋值 -> 新值:第二次赋值
- map 映射
class Site(val map: Map<String, Any?>) { val name: String by map val url: String by map } fun main(args: Array<String>) { // 构造函数接受一个映射参数 val site = Site(mapOf( "name" to "Google Cloud", "url" to "www.google.com" )) // 读取映射值 println(site.name) println(site.url) } //结果 Google Cloud www.google.com
- Not Null
class Foo { var notNullBar: String by Delegates.notNull<String>() } //若是不赋值,就print访问,则会抛异常 foo.notNullBar = "bar" println(foo.notNullBar)
- 局部委托属性
尴尬,粗略的看一遍,真的有点累,以后慢慢充实….