kotlin基础语法

一、基本数据类型

分一下几种进行介绍。

  • 数值类型
  • 字符类型
  • Boolean

1.1 数值类型

Kotlin 的基本数值类型包括 ByteShortIntFloatLongDouble

数据类型 位长
Byte 8
Short 16
Int 32
Float 32
Long 64
Double 64

分一下几种情况分别进行说明:

  • 数值类型使用;
  • 数值类型 二、八、十六进制转换;
  • 数值比较;
  • 数值类型转换;
  • 数值类型位操作;

1.1.1 数值类型使用

基本数值类型( ByteShortIntFloatLongDouble)使用。

数值写法:

// Int:数值类型变量定义
// var <标识符> : <类型> = <初始化值>
val aa: Int = 123
val aa = 123

// Float: 使用 f 或者 F 后缀
val aa = 123.5f
val bb = 123.5F

// Long: 以大写的 L 结尾
val aa = 1234_5678_9012_3456L

// Double 默认写法
val aa = 123.5

1.1.2 二、八、十六进制

基本数值类型,二、八、十六进制支持情况。

// 十进制:123
val aa = 123

// 二进制:0b 开头
val aa = 0b00001011

// 八进制:暂不支持

// 十六进制:0x 开头
val aa = 0x0F

1.1.3 数值比较

Kotlin 中没有基础数据类型,只有封装的数字类型。
我们每定义的一个变量,其实 Kotlin 帮你封装了一个对象,这样可以保证不会出现空指针,所以在比较两个数字的时候,就有比较数据大小和比较两个对象是否相同的区别了。

在 Kotlin 中:

  • 三个等号 === 表示比较对象地址
  • 两个 == 表示比较两个值大小
// 地址空间比较(同一对象)
val a: Int = 10000
println(a === a) // true

// 地址空间比较(不同对象)
val boxedA: Int? = a
val anotherBoxedA: Int? = a
println(boxedA === anotherBoxedA) //  false 对象地址不一样

// 大小比较
println(boxedA == anotherBoxedA) // true,值相等

1.1.4 类型转换

由于不同的表示方式,较小类型并不是较大类型的子类型。
因此,较小的类型不能隐式转换为较大的类型,因此若 Byte 转换为 Int 需进行显示转换。

val b: Byte = 1  
// 显示转换
val i: Int = b.toInt() 

每种数据类型都有下面的这些方法,可以转化为其它的类型:

toByte(): Byte
toShort(): Short
toInt(): Int
toLong(): Long
toFloat(): Float
toDouble(): Double
toChar(): Char

1.1.5 位操作

// 左移位
shl(bits)
// 右移位
shr(bits) 
// 无符号右移位
ushr(bits) 

// 与
and(bits)
// 或
or(bits)
// 异或
xor(bits)
// 反向
inv() 

1.2 字符类型

Char 必需是单引号 ’ 包含起来的。比如普通字符 ‘0’,‘a’。

// 地址空间比较(同一对象)
val a: Char = '9'
// 显示转换
val i: Int = a.toInt() 

1.3 字符串

字符串变量声明:

// 单行字符串
val str = "123"
// 多行字符串
val text = """
    多行字符串
    多行字符串
    """ 

字符串使用方式:

// 取字符串中的字符
val str = "123"
println(str[2])  
// 取字符串中的字符
for (c in str) {
    println(c)
}

// 显示字符转换
val i: Int = str.toInt{) 

// 去处空格 
val text = """
    多行字符串
    多行字符串
    """.trimMargin()
	

字符串模板:

// $ 表示一个变量名或者变量值
// $varName 表示变量值
// ${varName.fun()} 表示变量的方法返回值

fun main(args: Array<String>) {
	var a = 1
	// 模板中的简单名称:
	val s1 = "a is $a" 

	// 模板中的任意表达式:
	val s2 = "${s1.replace("is", "was")}, now is $a"
	
    print(s2)
}

1.3 布尔

布尔用 Boolean 类型表示,它有两个值:true 和 false。

val b: Boolean = true

二、变量

  • 变量定义
  • 变量NULL检查
  • 变量类型检测 is
  • 变量区间表示
  • 可变长参数vararg

2.1 变量定义

  • var 可变变量;
  • val 只读变量,相当于java中的final变量,val 变量创建的时候必须初始化。
var <变量标识符> : <变量类型> = <变量初始化值>

// 可变变量初始化:
var a: Int = 1
// 只读变量初始化:
val b: Int = 2

2.2 变量NULL检查

  • 类型后面加? 表示可为空
    var aa: String? = "23"
  • 标识符后加!! 表示若为null 正常抛出异常;
    val aa = age!!.toInt()
  • 标识符后加? 表示若为空 则返回空
    val aa = age?.toInt()
  • ?:使用:age为空返回-1
    val ages2 = age?.toInt() ?: -1

2.3 变量类型检测

// 类型检测
if (obj is String) {
	// 做过类型判断以后,obj会被系统自动转换为String类型
	return obj.length 
}

2.4 变量区间

  • 使用 … 代表 rangeTo (从小到大)
    for (i in 1..4) print(i) // 输出“1234”
  • 使用 downTo (从大到小)
    for (i in 4 downTo 1 step 2) print(i) // 输出“42”
  • 使用 step 指定步长
    for (i in 1..4 step 2) print(i) // 输出“13”
  • 使用 until 排除结束元素
// i in [1, 10) 排除了 10
for (i in 1 until 10) {   
     println(i)
}

2.5 可变长参数

可边长参数 vararg

fun vars(vararg v:Int){
    for(vt in v){
        print(vt)
    }
}

// 测试
fun main(args: Array<String>) {
    vars(1,2,3,4,5)  // 输出12345
}

三、方法

  • 方法定义
  • lambda匿名方法

3.1 方法定义

方法定义使用关键字:fun
参数格式为:参数 : 类型

// 有返回值方法定义
fun sum(a: Int, b: Int): Int {
	// Int 参数,返回值 Int 
	return a + b 
}
// 无返回值方法定义
fun printSum(a: Int, b: Int) {
	print(a + b) 
}

3.2 lambda匿名方法

// 测试
fun main(args: Array<String>) {
    val sumLambda: (Int, Int) -> Int = {x,y -> x+y}
    println(sumLambda(1,2))  // 输出 3
}

四、条件控制

  • if表达式
  • when表达式

4.1 if表达式

一个 if 语句包含一个布尔表达式和一条或多条语句。

// 使用if  else 
var max: Int
if (a > b) {
    max = a
} else {
    max = b
}

// 使用 if 表达式
var max = a 
if (a < b) max = b
 
// 作为表达式使用:表达式结果赋值给变量
val max = if (a > b) a else b
// 作为表达式使用:表达式结果赋值给变量
val max = if (a > b) {
    a
} else {
    b
}

4.2 when表达式

when 将它的参数和所有的分支条件顺序比较,直到某个分支满足条件。
when 类似其他语言的 switch 操作符。
when 表达式 elseswitch 的 default,如果其他分支都不满足条件将会求值 else 分支。

//
when (x) {
    1 -> print("x == 1")
    2 -> print("x == 2")
    else -> { // 注意这个块
        print("x 不是 1 ,也不是 2")
    }
}
// 
when (x) {
    0, 1 -> print("x == 0 or x == 1")
    else -> print("otherwise")
}

when 表达式,用in检测一个值在或者不在区间或者集合中:

//
when (x) {
    in 1..10 -> print("x is in the range")
    in validNumbers -> print("x is valid")
    !in 10..20 -> print("x is outside the range")
    else -> print("none of the above")
}

when 表达式,用用is检测一个值是或者不是一个特定类型的值:

//
when(x) {
    is String -> x.startsWith("prefix")
    else -> false
}

五、循环控制

  • for 循环
  • while 与 do while
  • return、break与continue

5.1 for 循环

for 循环可以对任何提供迭代器(iterator)的对象进行遍历。

//
for (item in collection) print(item)
// 循环体也可以是一个语句
val items = listOf("apple", "banana", "kiwi")
for (item in items) println(item)
// 循环体也可以是一个代码块
val items = listOf("apple", "banana", "kiwi")
for (item in items) {
    println(item)
}

// 通过索引遍历一个数组或者一个 list
for (i in array.indices) {
    print(array[i])
}

// 使用 withIndex 库方法
for ((index, value) in array.withIndex()) {
    println("the element at $index is $value")
}

5.2 while 与 do while

whiledo while 功能相似;不同的是 do while 至少会执行一次。

fun main(args: Array<String>) {
    // while 循环
    var x = 5
    while (x > 0) {
        println( x--)
    }
	
    // do…while 循环
    var y = -1
    do {
        println(y--)
    } while(y>0)
}

// 执行结果
// while 循环
5
4
3
2
1
// do…while 循环
-1

5.3 return、break与continue

  • return 从最直接包围它的方法或者匿名方法返回;
  • break 终止最直接包围它的循环;
  • continue 继续下一次最直接包围它的循环;

使用方式举例如下:

fun main(args: Array<String>) {
    for (i in 1..10) {
        if (i==3) continue  // i 为 3 时跳过当前循环,继续下一次循环
        println(i)
        if (i>5) break   // i 为 6 时 跳出循环
    }
}

Kotlin中的标签
在 Kotlin 中任何表达式都可以用标签来标记。
标签的格式标识符后跟 @ 符号,例如:abc@、fooBar@都是有效的标签。

// 用标签限制 break
loop@ for (i in 1..100) {
    for (j in 1..100) {
        if (……) break@loop
    }
}

六、类

  • 类定义
  • 抽象类
  • 嵌套类
  • 内部类
  • 匿名内部类

6.1 类定义

  • 类关键词
    类使用关键字 class 声明,后面紧跟类名:
    class Person{}
  • 主构造方法
    类可以有一个 主构造器多个次构造器
    主构造器是类头部的一部分,位于类名称之后。主构造器中不能包含任何代码,初始化代码可以放在初始化代码段init中。
  • 次构造方法
    如果类有主构造方法,每个次构造方法都要 直接或间接通过另一个次构造方法 代理主构造方法;在同一个类中代理另一个构造方法使用 this 关键字;
  • 类属性声明
    类中非空属性必须在定义的时候初始化,但也可通过lateinit关键词延迟初始化。
    另外,Kotlin提供了后端变量field机制,但field关键词只能应用于属性的访问器。

Kotlin 类构造代码举例如下:

// 类关键词:类使用关键字 class 声明,后面紧跟类名;
// 构造方法:带构造方法的类 (constructor关键词 可省略);
class Person constructor (name: String){

    // 类属性:非空属性必须在定义的时候初始化;
    // 类属性:Kotlin提供了 后端变量field 机制,但 field关键词 只能应用于属性的访问器;
    var mName: String = "xia"
        get() = field.toUpperCase()   // 将变量赋值后转换为大写

    var mAge: Int = 16
        set(value) {
            if (value < 20) {       // 如果传入的值小于 20 返回该值
                field = value
            } else {
                field = 18         // 否则返回18
            }
        }
    
    // 构造方法:主构造器中不能包含任何代码,初始化代码可放在 init 代码段中;
    init {
        this.mName = name
        println("init name $mName")
        println("init age $mAge")
    }
    // 次构造方法:如果类有主构造方法,每个次构造方法都要 直接或间接通过另一个次构造方法 代理主构造方法;
    // 次构造方法:在同一个类中代理另一个构造方法使用 this 关键字;
    constructor (name: String, age:Int) : this(name) {
        // 次构造方法...
        this.mName = name
        this.mAge = age
        println("constructor init name $mName")
        println("constructor init age $mAge")
    }

}

// main 程序入口
fun main(args: Array<String>) {
	// 创建对象
    var person: Person = Person("xiaxl")

    // 姓名
    person.mName = "xiaxueliang"
    println("changeName:${person.mName}")

    // 年龄
    person.mAge = 21
    println("changeAge:${person.mAge}")

}

6.2 抽象类

抽象类 类本身、类中的部分成员,都可以声明为abstract类型。

// 抽象类 类本身、类中的部分成员,都可以声明为abstract类型
abstract class Person {
    abstract fun changeName()
}

6.3 嵌套类

可以把类嵌套在其他类中。

// 嵌套类:可以把类嵌套在其他类中
// 外部类
class Outer {                  
    private val outerProperty: Int = 1

    // 嵌套类
    class Person {             
        fun changeAge() = 18
    }
}

fun main(args: Array<String>) {
	// 嵌套类调用:外部类.嵌套类.嵌套类方法/属性
    val age = Outer.Person().changeAge() 
    
    println(age)    // 输出 18
}

6.4 内部类

内部类使用 inner 关键字来表示。
内部类会带有一个对外部类的对象的引用,所以可以访问外部类成员属性和成员方法。可以使用 this@label 标签,访问来自外部作用域的 this

// 内部类:内部类使用 inner 关键字来表示。
// 内部类:内部类会带有一个对外部类的对象的引用,所以可以访问外部类成员属性和成员方法。
class Outer {
    private val iProperty: Int = 18
    var sProperty = "outerName"

    // 内部类
    inner class Person {
        // 访问外部类成员
        fun test01() = iProperty  

        // 获取外部类对象,访问外部类成员
        fun test02() {
            // 内部类:使用 `this@label` 标签,访问来自外部作用域的 `this`
            var outerObj = this@Outer 
            // 访问外部类成员
            println("inner sProperty:" + outerObj.sProperty)
        }
    }
}

fun main(args: Array<String>) {

    val iProperty = Outer().Person().test01()
    println("main Person test01: " + iProperty)
    //
    val sProperty = Outer().Person().test02()   
    println("main Person test02: " + sProperty)
}

6.5 匿名内部类

// 定义接口
interface PersionInterFace {
    fun changeName()
}

// 定义类
class Persion {
    var v = "成员属性"
    
    // 定义方法
    fun setInterFace(testObj: PersionInterFace) {
        testObj.changeName()
    }
}


// 程序入口
fun main(args: Array<String>) {

    // 获取类的对象
    var persion = Persion()

    // 采用对象表达式来创建接口对象,即匿名内部类的实例。
    persion.setInterFace(object : PersionInterFace {
        override fun changeName() {
            println("实现匿名内部类的 changeName 方法")
        }
    })
}

七、继承

Kotlin 中所有类都继承该 Any 类,它是所有类的超类。
另外,Any 类默认提供了三个方法,分别为:equals()hashCode()toString()

  • 构造方法
  • 重写方法
  • 重写属性

7.1 构造方法

  • 如果子类有主构造方法:
    则必须在子类主构造方法初始化基类
// 如果子类有主构造方法:则必须在子类主构造方法中初始化基类。
// 基类
// 如果一个类要被继承,可以使用 open 关键字进行修饰
// Any 类为所有类的超类
open class Person(var name:String, var age:Int){

}
// 子类 Student
class Student(name:String, age:Int, ,number:String) : Person(name, age) {

}

// 测试
fun main(args: Array<String>) {
    var s =  Student("xia", 18, "n987456123")
    println("姓名: ${name}")
    println("年龄: ${age}")
    println("学号: ${number}")
}
  • 如果子类没有主构造方法:
    则必须在子类每一个次级构造方法中用 super 关键字初始化基类
// 如果子类没有有主构造方法:则必须在子类每一个次级构造方法中用 super 关键字初始化基类。
// 基类
open class Person(name:String){
    // 基类 次构造方法
    constructor(name:String,age:Int):this(name){
        println("--- 基类 次构造方法 ---")
        println("姓名: ${name}")
        println("年龄: ${age}")
    }
}

// 子类 Student
class Student:Person{

    // 子类 次构造方法
    constructor(name:String,age:Int,number:String):super(name,age){
        println("--- 子类 次级构造方法 ---")
        println("姓名: ${name}")
        println("年龄: ${age}")
        println("学号: ${number}")
    }
}

fun main(args: Array<String>) {
    var s =  Student("xia", 18, "number987456123")
}

7.2 重写方法

在基类中,声明函数方法时,此方法默认为 final ,不能被子类重写。
如果允许子类重写该方法,需要手动添加 open 修饰, 子类重写方法需使用 override 关键词。

// 基类
// 方法重写:在基类中,使用fun声明函数方法时,此函数方法默认为 final ,不能被子类重写。
// 方法重写:如果允许子类重写该方法,需要手动添加 open 修饰, 子类重写方法需使用 override 关键词。
open class Person{
	// 允许子类重写
    open fun study(){       
        println("学习使我快乐")
    }
}

// 子类
class Student : Person() {
	// 重写
    override fun study(){    
        println("我是一名学生")
    }
}

// 程序入口
fun main(args: Array<String>) {
    val s =  Student()
    s.study();

}

7.3 重写属性

使用 override 关键字,也可以在主构造函数中使用 override 关键字作为属性声明的一部分。

interface Persion {
    val age: Int
}

// 重写属性:使用 override 关键字
class Student1 : Persion {
    override var age: Int = 9
}
// 重写属性:在主构造函数中使用 override 关键字作为属性声明的一部分
class Student2(override val age: Int) : Persion{

}

// 程序入口
fun main(args: Array<String>) {
    var s1 =  Student1()
    println("Student1: "+s1.age);
    
    var s2 =  Student2(18)
    println("Student2: "+s2.age);
}

八、接口

Kotlin 使用 interface 关键字定义接口。

  • 接口中的方法允许有默认实现;
  • 接口中的属性只能是抽象的,不允许初始化;
  • 一个类可以实现一个或多个接口,实现多个接口时,可能会遇到同一方法继承多个实现的问题。
// 接口类A
interface AInterface {
	// 接口:属性只能是抽象的,不允许初始化
	var name:String 

	// 接口:方法允许有默认实现
	// 未实现的方法
    fun changeName()   
    // 已实现的方法 
    fun changeAge() {  
    	println("AInterface changeAge")
    }
}

// 接口类B
interface BInterface {
	// 接口:方法允许有默认实现
	// 未实现的方法
    fun changeName() {  
        println("BInterface changeName")
    }  
    // 已实现的方法 
    fun changeAge() {  
        println("BInterface changeAge")
    }
}
 
class C : AInterface {
	// 重写属性
	override var name: String = "xiaxl" 
    // 重写方法
    override fun changeName() { 
        println("Class C changeName")
    }   
}
 
class D : AInterface, BInterface {
	// 重写属性
	override var name: String = "xiaxl" 
    // 重写方法
    override fun changeName() {
        // super<AInterface>.changeName()
        super<BInterface>.changeName()

        println("Class D changeName")
    }
    // 重写方法
    override fun changeAge() {
        super<BInterface>.changeAge()

        println("Class D changeAge")
    }
}
 
fun main(args: Array<String>) {
    val d =  D()
    println(" ")
    d.changeName();
    println(" ")
    d.changeAge();
}

九、枚举

enum class Color(val rgbValue: Int) {
    RED(0xFF0000),
    GREEN(0x00FF00),
    BLUE(0x0000FF)
}

fun main(args: Array<String>) {
    // 
    var color:Color=Color.GREEN
    
    // 打印枚举名称
    println(color.name)
    // 打印枚举顺序
    println(color.ordinal)
    // 打印枚举 Value
    println(color.rgbValue)

}

十、数据类型

先说为什么要使用数据类
Kotlin使用数据类替换普通类的好处是Kotlin会帮我们产生大量代码,省去大量的重复性工作。

  • Kotlin中使用数据类只需要在class关键字前添加data关键字,编译器会自动从主构造函数中根据声明的属性推导出相关函数 equals()toString()copy()componentN()hashCode()
  • Kotlin中数据类 自动声明与构造函数入参同名的属性字段,自动实现每个属性字段的存取器方法set/get

声明数据类:

// 声明数据类型
data class <类名> <主构造函数参数> <:继承类和实现接口> { /*类体*/ }

数据类型举例:


// 创建数据类
data class Person(var name :String, var age :Int){
	// 类体内生命属性
	var job: String = "student"
}

// 程序入口
fun main(args: Array<String>) {
    var p = Person("xiaxl",18)

	// 调用数据类的 getter
    println(p.name) 
    println(p.age) 
    println(p.job) 

    // 调用数据类的 setter
    p.name = "xiaxueliang" 

    // 自动调用toString()方法
    println(p) 

    //调用hashCode()方法,输出内存地址
    println(p.hashCode())  

    // 解析构造函数 componentN
    val (name, age) = p
    println("name = $name, age=$age")
}

十一、泛型


// 泛型举例
class Persion<T>(t : T) {
    var value = t
}

// 泛型举例
fun <T> doPrintln(t: T) {
    when (t) {
        is Int -> println("整型数字: $t")
        is String -> println("字符串大写: ${t.toUpperCase()}")
        else -> println(" 不是整型,也不是字符串")
    }
}


// 程序入口
fun main(args: Array<String>) {
    var pInt = Persion<Int>(18)
    var pString = Persion<String>("xiaxl")

    // 输出
    doPrintln(pInt.value)
    doPrintln(pString.value)
}

十二、伴生对象

  • 伴生对象
  • 对象声明

12.1 伴生对象

伴生对象,就是随着类的存在而存在。
在Kotlin中,类是没有static方法的,在大多数情况下 Kotlin 推荐的做法是使用包级别的函数作为静态方法,Kotlin 会将包级别的函数当做静态方法来看待。

// 伴生对象
class MyConstant {
    companion object {
        var num: Int = 100
 
        fun doPrint() {
            println("method test")
        }
    }
}

// 程序入口
fun main(args: Array<String>) {

    // 变量
	var num = MyConstant.num
    println("num: $num")
    // 方法
    MyConstant.doPrint()
}

12.2 对象声明

Kotlin 可以通过对象声明获得一个单例,Kotlin 使用 object 关键字来声明一个对象。

// 对象声明
object Singleton{
    // 属性
    val name = "xia"
    // 方法
    fun doPrint(age: Int){
        println("$name ---- $age")
    }
}

// 程序入口
fun main(args: Array<String>) {
    // 单例
    Singleton.doPrint(18)
}

上一篇:虹科PCAN在工程机械中的应用


下一篇:python 函数调用传递参数的两个小技巧 列表前加*和字典前加**