Kotlin

Kotlin的文件:.kt

基本数据类型
类型位宽度
Double64
Float32
Long64
Int32
Short16
Byte8
定义常量:关键字 var
var <标识符> : <类型> = <初始化值>
var a : Int = 1
var b = 2
var c : Int
c = 3
定义变量:关键字 val
val <标识符> : <类型> = <初始化值>
函数定义:关键字fun,参数格式:参数:类型
//返回值类型是Int
fun	sum(a: Int, b:Int) : Int{
	return a + b
}
//自动推断返回值类型
fun sum(a : Int, b : Int) = a + b
//public 方法必须明确写出返回值类型
public fun sum(a : Int, b : Int) : Int = a + b 
//无返回值类似void
fun printSum(a: Int, b: Int): Unit { 
    print(a + b)
}

可变长参数的函数,关键字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表达式
fun main(args: Array<String>) {
    val sumLambda: (Int, Int) -> Int = {x,y -> x+y}
    println(sumLambda(1,2))  // 输出 3
}
字符串模板

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

var a = 1
// 模板中的简单名称:
val s1 = "a is $a"	  //a is 1

a = 2
// 模板中的任意表达式:
val s2 = "${s1.replace("is", "was")}, but now is $a"		//a was 1, but now is 2
NULL检查机制
//类型后面加?表示可为空, var age: String? = null 可以通过
var age: String? = "23" 
//抛出空指针异常
val ages = age!!.toInt()
//不做处理返回 null
val ages1 = age?.toInt()
//age为空返回-1
val ages2 = age?.toInt() ?: -1

以上情况 ,如果出现var age: String? = “null”,或var age: String? = " "
仍会报错

fun main(args: Array<String>) {
  if (args.size < 2) {
    print("Two integers expected")
    return
  }
  //将字符串转换为Int
  val x = parseInt(args[0])
  val y = parseInt(args[1])
  // 直接使用 `x * y` 会导致错误, 因为它们可能为 null.
  if (x != null && y != null) {
    // 在进行过 null 值检查之后, x 和 y 的类型会被自动转换为非 null 变量
    print(x * y)
  }
}
符号简单说明
?加在变量名后,系统在任何情况不会报它的空指针异常。
!!加在变量名后,如果对象为null,那么系统一定会报异常!
?:对象A ?: 对象B 表达式,意思为,当对象 A值为 null 时,那么它就会返回后面的对象 B
::表示把一个方法当做一个参数,传递到另一个方法中进行使用,通俗的来讲就是引用一个方法
->符号后边的值 是 前边表达式的返回值
类型检测及自动类型转换,使用 is 运算符
fun getStringLength(obj: Any): Int? {
  if (obj is String) {
    // 做过类型判断以后,obj会被系统自动转换为String类型
    return obj.length 
  }

  //在这里还有一种方法,与Java中instanceof不同,使用!is
  // if (obj !is String){
  //   // XXX
  // }

  // 这里的obj仍然是Any类型的引用
  return null
}

或者

fun getStringLength(obj: Any): Int? {
  if (obj !is String)
    return null
  // 在这个分支中, `obj` 的类型会被自动转换为 `String`
  return obj.length
}

或者

fun getStringLength(obj: Any): Int? {
  // 在 `&&` 运算符的右侧, `obj` 的类型会被自动转换为 `String`
  if (obj is String && obj.length > 0)
    return obj.length
  return null
}
区间
for (i in 1..4) print(i) // 输出“1234”

for (i in 4..1) print(i) // 什么都不输出

if (i in 1..10) { // 等同于 1 <= i && i <= 10
    println(i)
}

// 使用 step 指定步长
for (i in 1..4 step 2) print(i) // 输出“13”

for (i in 4 downTo 1 step 2) print(i) // 输出“42”


// 使用 until 函数排除结束元素
for (i in 1 until 10) {   // i in [1, 10) 排除了 10
     println(i)
}
比较两个数字

Kotlin 中没有基础数据类型,只有封装的数字类型
三个等号 === 表示比较对象地址,两个 == 表示比较两个值大小

fun main(args: Array<String>) {
    val a: Int = 10000
    val b: Int = 10000
    println(a === b) // true,值相等,对象地址相等

    //经过了装箱,创建了两个不同的对象
    val boxedA: Int? = a
    val anotherBoxedA: Int? = a

    //虽然经过了装箱,但是值是相等的,都是10000
    println(boxedA === anotherBoxedA) //  false,值相等,对象地址不一样
    println(boxedA == anotherBoxedA) // true,值相等
}
类型转换
val b: Byte = 1 // OK, 字面值是静态检测的
val a: Int = b // 错误
val c: Int = b.toInt() // OK

每种数据类型都有以下方法

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

对于Int和Long类型,还有一系列的位操作符可以使用,分别是:

shl(bits) – 左移位 (Java’s <<)
shr(bits) – 右移位 (Java’s >>)
ushr(bits) – 无符号右移位 (Java’s >>>)
and(bits) – 与
or(bits) – 或
xor(bits) – 异或
inv() – 反向
字符

Char 不能直接和数字操作,Char 必需是单引号 ’ 包含起来的。比如普通字符 ‘0’,‘a’。特殊字符可以用反斜杠转义

布尔

布尔用 Boolean 类型表示,它有两个值:true 和 false。
若需要可空引用布尔会被装箱。
内置的布尔运算有:
|| – 短路逻辑或
&& – 短路逻辑与
! - 逻辑非

数组

数组的创建两种方式:一种是使用函数arrayOf();另外一种是使用工厂函数

fun main(args: Array<String>) {
    //[1,2,3]
    val a = arrayOf(1, 2, 3)
    //[0,2,4]
    val b = Array(3, { i -> (i * 2) })

    //读取数组内容
    println(a[0])    // 输出结果:1
    println(b[1])    // 输出结果:2
}

除了类Array,还有ByteArray, ShortArray, IntArray,用来表示各个类型的数组,省去了装箱操作

字符串

方括号 [] 语法可以很方便的获取字符串中的某个字符,也可以通过 for 循环来遍历

//遍历
for (c in str) {
    println(c)
}

三个引号 “”" 扩起来的字符串,支持多行字符串

fun main(args: Array<String>) {
    val text = """
    多行字符串
    多行字符串
    """
    println(text)   // 按照字符串样式输出,包括前置空格
}

可以通过 trimMargin() 方法来删除多余的空白。默认 | 用作边界前缀,但你可以选择其他字符并作为参数传入,比如 trimMargin(">")

fun main(args: Array<String>) {
    val text = """
    |多行字符串
    |菜鸟教程
    |多行字符串
    |Runoob
    """.trimMargin()
    println(text)    // 前置空格删除了
}
字符串模板

模板表达式以美元符($)开头,由一个简单的名字构成

fun main(args: Array<String>) {
    val i = 10
    val s = "i = $i" // 求值结果为 "i = 10"
    println(s)
}

或者用花括号扩起来的任意表达式:

fun main(args: Array<String>) {
    val s = "runoob"
    val str = "$s.length is ${s.length}" // 求值结果为 "runoob.length is 6"
    println(str)
}

原生字符串和转义字符串内部都支持模板。 如果你需要在原生字符串中表示字面值 $ 字符(它不支持反斜杠转义),可以用下列语法

fun main(args: Array<String>) {
    val price = """
    ${'$'}9.99
    """
    println(price)  // 求值结果为 $9.99
}
IF 表达式
  1. 传统方式
  2. 将if表达式的结果赋值给一个变量
val max = if (a > b) {
    print("Choose a")
    a
} else {
    print("Choose b")
    b
}
  1. 简单实现
val c = if (condition) a else b

使用区间
使用 in 运算符来检测某个数字是否在指定区间内,区间格式为 x…y :

fun main(args: Array<String>) {
    val x = 5
    if (x in 1..8) {
        println("x 在区间内")
    }
}
When 表达式

when 类似其他语言的 switch 操作符

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

在 when 中,else 同 switch 的 default。如果其他分支都不满足条件将会求值 else 分支。

如果很多分支需要用相同的方式处理,则可以把多个分支条件放在一起,用逗号分隔:

when (x) {
    0, 1 -> print("x == 0 or x == 1")
    else -> print("otherwise")
}

也可以检测一个值在(in)或者不在(!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 也可以用来取代 if-else if链。 如果不提供参数,所有的分支条件都是简单的布尔表达式,而当一个分支的条件为真时则执行该分支:

when {
    x.isOdd() -> print("x is odd")
    x.isEven() -> print("x is even")
    else -> print("x is funny")
}

when 中使用 in 运算符来判断集合内是否包含某实例:

fun main(args: Array<String>) {
    val items = setOf("apple", "banana", "kiwi")
    when {
        "orange" in items -> println("juicy")
        "apple" in items -> println("apple is fine too")
    }
}
For 循环

for 循环可以对任何提供迭代器(iterator)的对象进行遍历,语法如下:

/**
* collection是被遍历的对象
* item是遍历出来的每一个对象
*/
for (item in collection) print(item)

循环体可以是一个代码块:

for (item: Int in ints) {
    // ……
}

通过索引遍历一个数组或者一个 list

for (i in array.indices) {
    print(array[i])
}

用库函数 withIndex:

for ((index, value) in array.withIndex()) {
    println("the element at $index is $value")
}

使用forEach :

//ints是要遍历的对象,it是遍历出来的每个对象
 ints.forEach {
        if (it == 0) return
        print(it)
    }
return返回

默认从最直接包围它的函数或者匿名函数返回
从最直接包围它的函数即 foo 中返回

fun foo() {
    ints.forEach {
        if (it == 0) return
        print(it)
    }
}

从 lambda 表达式中返回

fun foo() {
    ints.forEach lit@ {
        if (it == 0) return@lit
        print(it)
    }
}

使用隐式标签

fun foo() {
    ints.forEach {
        if (it == 0) return@forEach
        print(it)
    }
}

类定义

Kotlin 类可以包含:构造函数和初始化代码块、函数、属性、内部类、对象声明。

class Demo{  // 类名为 Demo
    // 大括号内是类体构成
}

可以像使用普通函数那样使用构造函数创建类实例:

val site = Demo() // Kotlin 中没有 new 关键字
 // 使用 . 号来引用 属性

构造器

//User类有多个构造器
class User{
    val username: String
    var age: Int
    constructor(username: String, age: Int){
        this.username = username
        this.age = age
    }
    constructor(username: String){
        this.username = username
        this.age = 10
    }
}
//Person类只有一个构造器
class Person(username: String, age: Int){
    private val username: String = username
    private var age: Int = age
    
}

多个构造器要使用constructor关键字

getter 和 setter

class Person {

    var lastName: String = "zhang"
        get() = field.toUpperCase()   // 将变量赋值后转换为大写
        set

    var no: Int = 100
        get() = field                // 后端变量
        set(value) {
            if (value < 10) {       // 如果传入的值小于 10 返回该值
                field = value
            } else {
                field = -1         // 如果传入的值大于等于 10 返回 -1
            }
        }

    var heiht: Float = 145.4f
        private set
}

// 测试
fun main(args: Array<String>) {
    var person: Person = Person()

    person.lastName = "wang"

    println("lastName:${person.lastName}")

    person.no = 9
    println("no:${person.no}")

    person.no = 20
    println("no:${person.no}")

}

输出结果

lastName:WANG
no:9
no:-1

Kotlin 中类不能有字段。提供了 Backing Fields(后端变量) 机制,备用字段使用field关键字声明,field 关键词只能用于属性的访问器

var age: Int = 100
        get() = field                // 后端变量
        set(value) {
            if (value < 10) {       // 如果传入的值小于 10 返回该值
                field = value
            } else {
                field = -1         // 如果传入的值大于等于 10 返回 -1
            }
        }

在kotlin里面,如果在类型声明之后没有使用符号?,则表示该变量不会为null。但是这个时候会要求我们初始化一个值。有些时候,我们在声明变量的时候,并不能初始化这个变量

kotlin提供了一种可以延迟初始化的方案,使用 lateinit 关键字描述属性:

public class MyTest {
    lateinit var subject: TestSubject

    @SetUp fun setup() {
        subject = TestSubject()
    }

    @Test fun test() {
        subject.method()  // dereference directly
    }
}

主构造器
主构造器中不能包含任何代码,初始化代码可以放在初始化代码段中,初始化代码段使用 init 关键字作为前缀。

class Person constructor(firstName: String) {
    init {
        println("FirstName is $firstName")
    }
}

抽象类

abstract class Bank {
    abstract fun simpleInterest(p: Int, r: Double, t: Int) :Double
}

class SBI : Bank() {
    override fun simpleInterest(p: Int, r: Double, t: Int): Double{
        return (p*r*t)/100
    }
}
class PNB : Bank() {
    override fun simpleInterest(p: Int, r: Double, t: Int): Double{
        return (p*r*t)/100
    }
}
fun main(args: Array<String>) {
    var sbi: Bank = SBI()
    val sbiint = sbi.simpleInterest(1000,5.0,3)
    println("SBI interest is $sbiint")
    var pnb: Bank = PNB()
    val pnbint = pnb.simpleInterest(1000,4.5,3)
    println("PNB interest is $pnbint")
}

非抽象的open成员函数可以在抽象类中重载。

open class Car {
    open fun run() {
        println("Car is running..")
    }
}
abstract class Honda : Car() {
    override abstract fun run()
}
class City: Honda(){
    override fun run() {
        //  TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
        println("Honda City is running..")
    }
}
fun main(args: Array<String>){
    val car = Car()
    car.run()
    val city = City()
    city.run()
}

内部类
内部类使用 inner 关键字来表示。
内部类会带有一个对外部类的对象的引用,所以内部类可以访问外部类成员属性和成员函数。

class Outer {
    private val bar: Int = 1
    var v = "成员属性"
    /**嵌套内部类**/
    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) //   1
    val demo2 = Outer().Inner().innerTest()   
    println(demo2)   // 内部类可以引用外部类的成员,例如:成员属性
}

类的修饰符

classModifier类属性修饰符,标示类本身特性。
abstract抽象类
final类不可继承,默认属性
enum枚举类
open类可继承,类默认是final的
annotation注解类
accessModifier访问权限修饰符
private仅在同一个文件中可见
protected同一个文件中或子类可见
public所有调用的地方都可见
internal同一个模块中可见

如果一个类要被继承,可以使用 open 关键字进行修饰。

构造函数
如果子类有主构造函数, 则基类必须在主构造函数中立即初始化。

open class Person(var name : String, var age : Int){// 基类

}

class Student(name : String, age : Int, var no : String, var score : Int) : Person(name, age) {

}

// 测试
fun main(args: Array<String>) {
    val s =  Student("Runoob", 18, "S12346", 89)
    println("学生名: ${s.name}")
    println("年龄: ${s.age}")
    println("学生号: ${s.no}")
    println("成绩: ${s.score}")
}

如果子类没有主构造函数,则必须在每一个二级构造函数中用 super 关键字初始化基类,或者在代理另一个构造函数。初始化基类时,可以调用基类的不同构造方法

/**用户基类**/
open class Person(name:String){
    /**次级构造函数**/
    constructor(name:String,age:Int):this(name){
        //初始化
        println("-------基类次级构造函数---------")
    }
}

/**子类继承 Person 类**/
class Student:Person{

    /**次级构造函数**/
    constructor(name:String,age:Int,no:String,score:Int):super(name,age){
        println("-------继承类次级构造函数---------")
        println("学生名: ${name}")
        println("年龄: ${age}")
        println("学生号: ${no}")
        println("成绩: ${score}")
    }
}

fun main(args: Array<String>) {
    var s =  Student("Runoob", 18, "S12345", 89)
}

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

如果有多个相同的方法(继承或者实现自其他类,如A、B类),则必须要重写该方法,使用super范型去选择性地调用父类的实现。

open class A {
    open fun f () { print("A") }
    fun a() { print("a") }
}

interface B {
    fun f() { print("B") } //接口的成员变量默认是 open 的
    fun b() { print("b") }
}

class C() : A() , B{
    override fun f() {
        super<A>.f()//调用 A.f()
        super<B>.f()//调用 B.f()
    }
}

fun main(args: Array<String>) {
    val c =  C()
    c.f();

}
Kotlin 扩展

Kotlin 可以对一个类的属性和方法进行扩展,且不需要继承或使用 Decorator 模式。
扩展是一种静态行为,对被扩展的类代码本身不会造成任何影响。
扩展函数
扩展函数可以在已有类中添加新的方法,不会对原类做修改,扩展函数定义形式:

fun receiverType.functionName(params){
    body
}
  • receiverType:表示函数的接收者,被扩展的对象
  • functionName:扩展函数的名称
  • params:扩展函数的参数,可以为NULL
// 扩展函数 swap,调换不同位置的值
fun MutableList<Int>.swap(index1: Int, index2: Int) {
    val tmp = this[index1]     //  this 对应该列表
    this[index1] = this[index2]
    this[index2] = tmp
}

fun main(args: Array<String>) {

    val l = mutableListOf(1, 2, 3)
    // 位置 0 和 2 的值做了互换
    l.swap(0, 2) // 'swap()' 函数内的 'this' 将指向 'l' 的值

    println(l.toString())  //[3, 2, 1]
}

扩展一个空对象
在扩展函数内, 可以通过 this 来判断接收者是否为 NULL,这样,即使接收者为 NULL,也可以调用扩展函数。例如

fun Any?.toString(): String {
    if (this == null) return "null"
    // 空检测之后,“this”会自动转换为非空类型,所以下面的 toString()
    // 解析为 Any 类的成员函数
    return toString()
}
fun main(arg:Array<String>){
    var t = null
    println(t.toString())
}

伴生对象的扩展
如果一个类定义有一个伴生对象 ,你也可以为伴生对象定义扩展函数和属性。

伴生对象通过"类名."形式调用伴生对象,伴生对象声明的扩展函数,通过用类名限定符来调用

class MyClass {
    companion object { }  // 将被称为 "Companion"
}

fun MyClass.Companion.foo() {
    println("伴随对象的扩展函数")
}

val MyClass.Companion.no: Int
    get() = 10

fun main(args: Array<String>) {
    println("no:${MyClass.no}")
    MyClass.foo()
}
数据类

Kotlin 可以创建一个只包含数据的类,关键字为 data:

data class User(val name: String, val age: Int)

编译器会自动的从主构造函数中根据所有声明的属性提取以下函数:

  • equals() / hashCode()
  • toString() 格式如 “User(name=John, age=42)”
  • componentN() functions 对应于属性,按声明顺序排列
  • copy() 函数

如果这些函数在类中已经被明确定义了,或者从超类中继承而来,就不再会生成。

为了保证生成代码的一致性以及有意义,数据类需要满足以下条件:

  • 主构造函数至少包含一个参数。

  • 所有的主构造函数的参数必须标识为val 或者 var ;

  • 数据类不可以声明为 abstract, open, sealed 或者 inner;

  • 数据类不能继承其他类 (但是可以实现接口)。

复制使用 copy() 函数,我们可以使用该函数复制对象并修改部分属性

data class User(val name: String, val age: Int)


fun main(args: Array<String>) {
    val jack = User(name = "Jack", age = 1)
    val newJack = jack.copy(age = 2)
    println(jack)
    println(newJack )

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值