Kotlin基础语法总结

基础数据类型

Kotlin 的基本数据类型包括 Byte、Short、Int、Float、Long、Double 、Boolean。

在java中基本数据类型一共有八种分别包括整形:byte、short、int、long,浮点型:float、double , 字符型 char
以及布尔型 boolean,不同于 Java 的是,Kotlin 字符不属于数值类型,是一个独立的数据类型。

大家可以看出Kotlin的基本数据类型和java不一样,Kotlin 中其实没有基础数据类型,只有封装的对象类型,你每定义的一个变量,其实Kotlin 帮你封装了一个对象,这样可以保证不会出现空指针。

对应Java中八大基本数据类型的对象类型、java在使用基础数据类型时候可以通过装箱操作封装成对象;

字符

和 Java 不一样,Kotlin 中的 Char 不能直接和数字操作(可以通过显示转换char.toInt()),Char 必需是单引号 ’ 包含起来的。比如普通字符 ‘0’,‘a’,字符字面值用单引号括起来: ‘1’。 特殊字符可以用反斜杠转义。 支持这几个转义序列:\t、 \b、\n、\r、\'、\"、\\ 和 \$。

当需要可空引用时,像数字、字符会被装箱。装箱操作不会保留同一性。

布尔

   布尔用 Boolean 类型表示,它有两个值:true 和 false。当需要可空引用布尔会被装箱。

数组

  数组用Array类实现,具有size属性、get、setf方法,由于使用 \[\] 重载了 get 和 set 方法,所以我们可以通过下标很方便的获取或者设置数组对应位置的值。java中素组具有length属性以及使用\[\]通过下标方式访问属性;

数组的创建两种方式:一种是使用函数arrayOf();另外一种是使用工厂函数。如下所示,我们分别是两种方式创建了两个数组:

fun main(args: Array) {

//\[1,2,3\]

val a = arrayOf(1, 2, 3)

//读取数组内容

println(a\[0\])    // 输出结果:1

println(b\[1\])    // 输出结果:2

}

val x: IntArray = intArrayOf(1, 2, 3

除了类Array,还有ByteArray, ShortArray, IntArray,LongArray等用来表示各个类型的数组,省去了装箱操作,因此效率更高,其用法同Array一样:

字符串

和java一样,String属于不可变的,Kotlin可以通过[]很方便访问对应下标字符,java中通过chatAt方法或者subString等方式获取对应字符,Kotlin中String支持遍历形式访问其中的字符,这一点很使用;

for (c in str) {

println(c)

}

另外Kotlin 支持三个引号 “”" 扩起来的字符串,支持多行字符串,比如:

fun main(args: Array) {

val text = """多行字符串 多行字符串"""

println(text)  // 输出

}

String 可以通过 trim(),trimEnd(),trimStart(),trimMargin() 等方法来删除多余的空白。

类型转换

java中可以通过隐式类型转换,数值大的类型可以转换成数据小的类型,但是这样往往会丢失精度,在kotlin中由于不同的表示方式,较小类型并不是较大类型的子类型,较小的类型不能隐式转换为较大的类型。

这意味着在不进行显式转换的情况下我们不能把 Byte 型值赋给一个 Int 变量,

val b: Byte = 1 // OK, 字面值是静态检测的

val i: Int = b.toInt() // OK

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

toByte(): Byte

toShort(): Short

toInt(): Int

toLong(): Long

toFloat(): Float

toDouble(): Double

toChar(): Char

变量、常量声明

定义只读变量使用关键字val定义,只能为其赋值一次,

val a: Int = 1 // 立即赋值 (非空属性必须在定义时候初始化,)

val b = 2 // 自动推断出 `Int` 类型 (非空属性必须在定义时候初始化,)

可重新赋值的变量使用var关键字:

var x = 5 // 自动推断出 `Int` 类型 (非空属性必须在定义时候初始化,)

x += 1

Kotlin语法支持类型自动推断,在声明变量或者常量的时候可以不用指定其类型,编译器在编译时候会为我们指定其类型;

非空属性必须在定义的时候初始化,kotlin提供了一种可以延迟初始化的方案,使用 lateinit 关键字描述属性:

var count:Int?=null //可空属性可以在后面赋值

count=5

lateinit var name : String 非空属性使用延迟初始化

函数定义

函数定义使用fun关键字,参数格式为 参数:类型 ,最后函数返回值类型,如下

fun sum( a:Int, b:Int):Int{return a+b}

亦可以函数表达式声明函数

fun sum( a:Int, b:Int)= a+b // 自动类型推断或者 fun sum(a:Int,b:Int):Int=a+b

无返回值的函数定义(类似Java中的void):

fun printSum(a: Int, b: Int): Unit {

print(a + b)

}

Unit返回类型可以省略:

public fun printSum(a: Int, b: Int) {

print(a + b)

}

可变长参数函数

可变长参数用vararg关键字进行修饰:

fun print(vararg v:Int){

    for(a in v){ 

        println("$a")

    }

}

字符串模板

$ 表示一个变量名或者变量值

$varName 表示变量值

${varName.fun()} 表示变量的方法返回值:

var a = 1

// 模板中的简单名称:

val s1 = “a is $a”

a = 2

// 模板中的任意表达式:

val s2 = “${s1.replace(“is”, “was”)}, but now is $a”

// 运行结果:a was 1, but now is 2

条件表达式

看一个常用if表达式:

fun value(a:Int, b :Int):Int {

if(a>b) {

return a+b 

}else{

return a-b   

}

}

通过条件表达式可以:

fun value(a:Int,b:Int)=if(a>b) a+b else a-b

NULL检查机制

Kotlin的空安全设计对于声明可为空的参数,在使用时要进行空判断处理,有两种处理方式,字段后加!!像Java一样抛出空异常,另一种字段后加?可不做处理返回值为 null或配合?:做空判断处理

//类型后面加?表示可为空

var age: String? = “23”

//抛出空指针异常

val ages = age!!.toInt()

//不做处理返回 null

val ages1 = age?.toInt()

//age为空返回-1

val ages2 = age?.toInt() ?: -1

当一个引用可能为 null 值时, 对应的类型声明必须明确地标记为可为 null。

**Kotlin 的NULL机制旨在消除来自代码空引用的危险,**这在java语言中属于最常见的陷阱之一,也就是访问空引用的成员会导致空引用异常。经常会抛出NullPointerException或简称NPE。

在Kotlin中经常会有一些链式调用用法,安全调用在链式调用中很有用,如:person?.class?.countName 如果调用链中任何一个属性值出现null情况,调用链会直接返回null,

后面属性不会出现NPE异常。

如果相对非null值执行某个操作,可以结合let操作符一起使用:

val listWithNulls: List<String?> = listOf(“Kotlin”, null)

for (item in listWithNulls) {

      item?.let { 

      println(it) // 输出 Kotlin 并忽略 null 

}

}

同样安全调用也可以出现在赋值的左侧,如果调用链中的任何一个接收者为空都会跳过赋值,而右侧的表达式根本不会求值:

person?.class?.mathTeacher = TeachManager.getTeacher()

** Elvis 操作符**

当我们有一个可空的引用r时,我们可以说“如果r非空,我使用它;否则使用某个非空的值x”:

val l: Int = if (b != null) b.length else -1

或者

fun value(a:Int,b:Int)=if(a>b) a+b else a-b

//条件表达式用法

除了完整的 if-表达式,这还可以通过 Elvis 操作符表达,写作**?😗*:

val l = b?.length ?: -1

如果?:左侧表达式非空,elvis 操作符就返回其左侧表达式,否则返回右侧表达式。请注意,当且仅当左侧为空时,才会对右侧表达式求值。

这种Elvis用法类似java语言中三元操作符;

public int value(int a,int b){return a>b?a+b :a-b}

等同于:

fun value(a:Int,b:Int)=if(a>b) a+b else a-b

val l = b?.length ?: -1

等同于:

final int l= b!=null? b.length : -1

类型检测及类型转换

如果对象不是目标类型,那么常规类型转换可能会导致ClassCastException,

val aInt: Int? = a as Int

为了避免类型转成异常,另一个选择是使用安全的类型转换,如果尝试转换不成功则返回 null:

val aInt: Int? = a as? Int

或者

val sInt :Int?= if(a is Int) a as Int else null

或者

val sInt :Int?= if(a is Int) a else null

is 运算符检测一个表达式是否某类型的一个实例(类似于Java中的instanceof关键字), 如果一个不可变的局部变量或属性已经判断出为某类型,那么检测后的分支中可以直接当作该类型使用,无需显式转换:

fun getStringLength(obj: Any): Int? {

if (obj is String) {

// 做过类型判断以后,obj会被系统自动转换为String类型

return obj.length

}

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

return null

}

或者

fun getStringLength(obj: Any): Int? {

if (obj !is String)

return null

// 在这个分支中, `obj` 的类型会被自动转换为 `String`

return obj.length

}

可空类型的集合

如果你有一个可空类型元素的集合,并且想要过滤掉空元素,你可以使用filterNotNull来实现:

val listOf = listOf<Int?>(1, 2, null, 4)

val intList: List = listOf.filterNotNull()

区间

区间表达式由具有操作符形式 … 的 rangeTo 函数辅以 in 和 !in 形成。

使用 in 运算符来检测某个数字是否在指定区间内:

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)

}

val list = listOf(“a”, “b”, “c”)

if (-1 !in 0…list.lastIndex) {

println("-1 is out of range")

}

if (list.size !in list.indices) {

println("list size is out of valid list indices range, too")

}

使用 for 循环

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

同样,kotlin的for循环中使用的也是in操作符,

val items = listOf(“dog”, “cat”, “pig”)

for (item in items) {

println(item)

}

或者通过索引

val items = listOf(“apple”, “banana”, “kiwifruit”)

for (index in items.indices) {

println("item at $index is ${items\[index\]}")

}

val array=arrayOf(“a”,“b”,“c”)

for (i in array.indices) {

print(array\[i\])

}

使用 when 表达式

when 将它的参数和所有的分支条件顺序比较,直到某个分支满足条件,

when 既可以被当做表达式使用也可以被当做语句使用。如果它被当做表达式,符合条件的分支的值就是整个表达式的值,如果当做语句使用, 则忽略个别分支的值。

when 类似java语言的 switch 操作符。其最简单的形式如下:

fun describe(obj: Any): String =

when (obj) {

1 -> “One”

“Hello” -> “Greeting”

is Long -> “Long”

!is String -> “Not a string”

else -> “Unknown”

}

在Kotlin中Any类是所有类的超类,类似java中的Object;

在 when 中,else 同 switch 的 default。如果其他分支都不满足条件将会求值 else 分支。如果很多分支需要用相同的方式处理,则可以把多个分支条件放在一起,用逗号分隔。

when 也可以用来取代 if-else if链。 如果不提供参数,所有的分支条件都是简单的布尔表达式,而当一个分支的条件为真时则执行该分支:

when {

x.isOdd() -> print("x is odd")

x.isEven() -> print("x is even")

else -> print("x is funny")

}

使用 lambda 表达式来过滤(filter)与映射(map)集合

val fruits = listOf(“banana”, “avocado”, “apple”, “kiwifruit”)

fruits.filter { it.startsWith(“a”) }

.sortedBy { it }

.map { it.toUpperCase() }

.forEach { println(it) }

结果:APPLE AVOCADO

类以及构造器

类的修饰符包括 classModifier 和_accessModifier_:

classModifier: 类属性修饰符,标示类本身特性。

abstract // 抽象类

final // 类不可继承,默认属性

enum // 枚举类

open // 类可继承,类默认是final的

annotation // 注解类

accessModifier: 访问权限修饰符

private // 仅在同一个文件中可见

protected // 同一个文件中或子类可见

public // 所有调用的地方都可见

internal // 同一个模块中可见

主构造器(主构造函数)

Koltin 中的类可以有一个 主构造器,以及一个或多个次构造器,主构造器是类头部的一部分,位于类名称之后:

class Person constructor(firstName: String) {}

如果主构造器没有任何注解,也没有任何可见度修饰符,那么constructor关键字可以省略。

class Person(firstName: String) {

}

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

class Person constructor(firstName: String) {

init {

  firstName="Scus"  //主构造器的参数可以在初始化代码段中使用

}

}

另外可以通过主构造器来定义属性并初始化属性值(可以是var或val):

class People(val firstName: String, val lastName: String) {

//...

}

如果一个非抽象类没有声明构造函数(主构造函数或次构造函数),它会产生一个没有参数的构造函数。构造函数是 public 。如果你不想你的类有公共的构造函数,你就得声明一个空的主构造函数:

class DontCreateMe private constructor () {

}

次构造函数

类也可以有二级构造函数,需要加前缀 constructor:

class Person(val name: String) {

constructor (name: String, age:Int) : this(name) {

    // 初始化...

}

}

抽象类

抽象是面向对象编程的特征之一,类本身,或类中的部分成员,都可以声明为abstract的。抽象成员在类中不存在具体的实现

注意:无需对抽象类或抽象成员标注open注解。

open class Base {

open fun f() {}

}

abstract class Derived : Base() {

override abstract fun f()

}

嵌套类

我们可以把类嵌套在其他类中,看以下实例:

class Outer { // 外部类

private val bar: Int = 1

class Nested {            // 嵌套类

    fun foo() = 2

}

}

匿名内部类

使用对象表达式来创建匿名内部类:

test.setInterFace(object : TestInterFace {

    override fun test() {

        println("对象表达式创建匿名内部类的实例")

    }

})
内部类

内部类使用 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)

    }

}

}

数据类

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

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

密封类

密封类用来表示受限的类继承结构:当一个值为有限几种的类型, 而不能有任何其他类型时。在某种意义上,他们是枚举类的扩展:枚举类型的值集合 也是受限的,但每个枚举常量只存在一个实例,而密封类 的一个子类可以有可包含状态的多个实例。

声明一个密封类,使用 sealed 修饰类,密封类可以有子类,但是所有的子类都必须要内嵌在密封类中。

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

}

接口

Kotlin 接口与 Java 8 类似,使用 interface 关键字定义接口,允许方法有默认实现:

interface MyInterface {

fun bar() // 未实现   

fun foo() {//已实现     

    println("foo") 

}

}

一个类或者对象可以实现一个或多个接口。

class Child : MyInterface {

override fun bar() {

    // 方法体

}

override fun fool() {

    // 方法体

}

}

接口中的属性只能是抽象的,不允许初始化值,接口不会保存属性值,实现接口时,必须重写属性:

interface MyInterface{

var name:String //name 属性, 抽象的

}

class MyImpl:MyInterface{

override var name: String = "runoob" //重写属性

}

Kotlin 继承

kotlin 中所有类都继承该 Any 类,它是所有类的超类,对于没有超类型声明的类是默认超类:

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

open class Base(p: Int) // 定义基类

class Derived(p: Int) : Base§

注意:Any 不是 java.lang.Object。

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

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

}

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

}

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

class Student : Person {

constructor(ctx: Context) : super(ctx) {

    }

   constructor(ctx: Context, attrs: AttributeSet) : super(ctx,attrs) {

    }

}

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

对象声明

Kotlin 使用 object 关键字来声明一个对象。

Kotlin 中我们可以方便的通过对象声明来获得一个单例。

object DataProviderManager {

fun registerDataProvider(provider: DataProvider) {

    // ……

}

val allDataProviders: Collection<DataProvider>

    get() = // ……

}

引用该对象,我们直接使用其名称即可:

DataProviderManager.registerDataProvider(……)

当然你也可以定义一个变量来获取获取这个对象,当你定义两个不同的变量来获取这个对象时,你会发现你并不能得到两个不同的变量,也就是说通过这种方式,我们获得一个单例:如

object Site {

var url:String = ""

val name: String = "菜鸟教程"

}

fun main(args: Array) {

var s1 =  Site

var s2 = Site

s1.url = "www.runoob.com"

println(s1.url)

println(s2.url)

}

// 输出结果都为"www.runnoob.com"

伴生对象

类内部的对象声明可以用 companion 关键字标记,这样它就与外部类关联在一起,我们就可以直接通过外部类访问到对象的内部元素。

class MyClass {

companion object Factory {

    fun create(): MyClass = MyClass()

}

}

val instance = MyClass.create() // 访问到对象的内部元素

我们可以省略掉该对象的对象名,然后使用 Companion 替代需要声明的对象名:

class MyClass {

companion object {

}

}

**注意:**一个类里面只能声明一个内部关联对象,即关键字 companion 只能使用一次。

伴生对象的成员看起来像java的静态成员,但在运行时他们仍然是真实对象的实例成员。

最后

如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
在这里插入图片描述
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

全套视频资料:

一、面试合集

在这里插入图片描述
二、源码解析合集
在这里插入图片描述

三、开源框架合集
在这里插入图片描述
欢迎大家一键三连支持,若需要文中资料,直接扫描文末CSDN官方认证微信卡片免费领取↓↓↓

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值