Kotlin基础知识

简介: kotlin基础知识

一、     

1.       可以声明一个空类

class Empty

java 中即使是空类,也需要写类体

2.       构造器

关键字constructor,当主构造函数没有任何注解或者可见性修饰符,可以省略。

class Person constructor(name: String, age: Int)

等价于
       class Person(name: String, age: Int)

3.       主构造函数与次构造函数

主构造函数:定义在类头中的构造函数。

次构造函数:定义在类体中的构造函数。

 

在 Kotlin 中,类中可以声明主构造函数(零个或一个)和次构造函数(零个或多个)。次构造函数可以使用this关键字调用其他的构造函数,没有主构造函数也可以写次构造函数,这时次构造函数不需要委托给主构造函数(无主构造函数)。

l  主构造函数只能调用父类的构造函数

l  次构造函数可以调用其他次构造函数(次构造函数不能循环调用),但是如果存在主构造函数,最终需要调用主构造函数。

l  如果一个类中,没有主构造函数,但是有init初始化块和次构造函数,那么在调用这个类的次构造函数时,init初始化块会在次构造函数执行前执行。

 

在主构造函数中定义的属性,如果有var或val修饰,就相当于类的属性,可以在类内部使用,如果没有修饰,则只能在init初始化块和类体内声明的属性初始化器中使用。次构造函数中的参数不能用val与var修饰。

类头中声明的主构造函数中不能包含任何的代码,只能放在init初始化块中。

 

Q:与swift的区别?

l  kotlin中只能有一个主构造器,多个次构造器。

l  swift中可以有多个指定构造器,多个便利构造器。

     

Q:kotlin中为什么有主构造函数和次构造函数之分?

个人理解:在主构造函数中,可以声明并初始化类中全部的存储属性,可以简化类结构。而且次构造函数最终需要调用主构造函数,这样可以保证类中的存储属性都可以完成初始化。但如果次构造函数也可以声明成员属性,那么就不能保证所有的属性都可以初始化,类结构会比较混乱。所以构造函数需要有主次之分。

没有主构造函数,只有多个次构造函数的情况,是为了方便从java转到kotlin开发的程序员使用。

4.       可以使用无参方式调用构造函数初始化实例对象的条件:

下列条件满足一条即可:

l  类中没有任何一个构造函数(主构造函数、次构造函数)

l  主构造函数所有参数都有默认值。

l  有一个次构造函数所有参数都有默认值。

 

       注意:

a)        只有主构造函数的参数全部都有默认值的情况下,编译器才会真正的生成一个无参构造方法,此时,通过无参方式调用构造函数创建实例对象时,会调用有默认值的有参构造函数,并不会调用额外生成的无参构造函数,这个额外的无参构造函数主要是给java代码中用的。

b)        次构造函数的参数都有默认值的时候,不会额外生成无参构造函数,只是在调用的时候,使用参数的默认值。

5.       利用可见性修饰符修饰主构造函数

默认可见为public,可以用private修饰  ---->引申,java中单例

Q:主构造函数用private修饰,并且无参数,当有一个次构造函数全部参数都有默认值时,会怎样?

A:在代码中可以使用无参构造函数创建对象。应该避免这么做!!!

 

二、      属性和字段

1.       声明属性

在kotlin中属性分为只读属性和可变属性两种,只读属性用关键字val声明,可变属性用关键字var 声明。

2.       Getters 和 Setters

kotlin中可以给属性设置自定义的get和set访问器。多用于计算属性。

 

get与set可以任意设置,只有get、只有set或者都有。

l  当getter可以推断出属性类型时,可以省略类型声明。

l  在get与set内部,不可以用属性名本身执行语句(互相引用,导致栈溢出),如果想使用本身属性值时,需要用幕后字段field。

l  可以对get与set进行可见性修饰和加注解

a)        getter必须与属性可见性一致

b)        setter可以随意设置,但是不会超出类的可见性

     

get() = 可以使用函数执行结果赋值

set(value) = 后面可以加if、when、try/catch表达式

 

3.       幕后字段backing field

Kotlin 中类不能有字段。然而,当使用自定义访问器时,有时有一个幕后字段(backing field)有时是必要的。为此 Kotlin 提供一个自动幕后字段,它可通过使用 field 标识符访问。

field 标识符只能用在属性的访问器内。

如果属性至少一个访问器使用默认实现,或者自定义访问器通过 field 引用幕后字段,将会为该属性生成一个幕后字段。

4.       幕后属性

个人理解:类似于幕后字段的手动实现,可控性强。

5.       编译期常量

已知值的属性可以使用 const 修饰符标记为编译期常量,可以使用在注解中。这些属性需要满足以下要求:

l  指定定义在顶层、 object或伴生对象中;

l  用 String 或原生类型 值初始化;

l  没有自定义 getter。

 

6.       延迟初始化属性

关键字lateinit,该修饰符只能用于在类体中(不是在主构造函数中)声明的 var 属性,并且仅当该属性没有自定义 getter setter 时。该属性必须是非空类型,并且不能是原生类型

在使用延迟初始化属性之前,必须要初始化,否则将会抛出异常。

 

使用延迟初始化属性,是因为有些情况下,在声明属性的时候不能确定该属性的初始化值,但是在后续的程序中,一定可以为其设置一个初始化值。lateinit修饰的属性,需要程序员保证非空!

 

使用场景:在Android中,需要声明页面的组件,在没有findViewById时,需要设置一个null初始化值,如果用lateinit修饰,则可以不设置null。

 

在后面会有一个延迟属性,只可以用val声明,一般用于声明一些初始化耗时的计算属性,只有在第一次访问时计算并将计算结果保存为属性值,再次访问时,会直接使用保存的值,不会再次计算。示例代码如下:

val lazyValue: String by lazy { println("computed!") "Hello" }

引申:声明属性时,默认初始化值在什么情况下可以不设置?

l  属性用abstract或lateinit修饰时。

l  声明在接口中的属性

l  扩展的属性

l  属性的所有自定义访问器都没有用过幕后字段field

 

7.       使用函数、匿名函数、lambda表达式给属性赋值

class SetValue {
   
val funReturnUnit = returnUnit()  //用无返回值的函数给属性赋值
    val funReturnValue = returnValue()//用有返回值的函数给属性赋值
   
val lambda = { "lambda表达式" }   //用lambda表达式给属性赋值
   
val lambdaRun = { "运行的lambda表达式" }()  //用lambda表达式执行结果给属性赋值
   
val anonymousFun = fun(): String {   //用匿名函数给属性赋值
       
return "匿名函数"
   
}                                         
   
val anonymousFunRun = fun(): String { //用匿名函数执行结果给属性赋值
       
return "执行的匿名函数"
   
}()                                      
    fun returnValue(): String {
       
return "有返回值的函数"
   
}
   
fun returnUnit() {}
}

fun main(args: Array<String>) {
   
val impl = SetValue()
    println(
"用无返回值的函数给属性赋值:         ${impl.funReturnUnit}")
    println(
"用有返回值的函数给属性赋值:         ${impl.funReturnValue}")
    println(
"用lambda表达式给属性赋值:           ${impl.lambda}")
    println(
"用lambda表达式执行结果给属性赋值:   ${impl.lambdaRun}")
    println(
"用匿名函数给属性赋值:               ${impl.anonymousFun}")
    println(
"用匿名函数执行结果给属性赋值:       ${impl.anonymousFunRun}")
}

 

程序执行结果如下:

      用无返回值的函数给属性赋值:          kotlin.Unit

      用有返回值的函数给属性赋值:          有返回值的函数

      用lambda表达式给属性赋值:           () -> kotlin.String

      用lambda表达式执行结果给属性赋值:   运行的lambda表达式

      用匿名函数给属性赋值:                () -> kotlin.String

      用匿名函数执行结果给属性赋值:        执行的匿名函数

 

三、      继承

1.       继承

继承是强耦合的!

kotlin中所有类都隐式继承自Any。类的默认修饰符是final,如果想要可以被继承,那么需要显式声明为open。

如果子类中不存在主构造函数时,可以在子类的次构造函数中使用super关键字初始化其基类型,调用父类的构造函数。

2.       子类调用父类的构造方法

子类的构造函数最终要调用父类构造函数。

l  当子类中存在主构造函数或者不存在任何构造函数时,需要在类头初始化父类,父类后有括号。

l  当子类中只存在次构造函数时,需要在次构造函数后面用super调用父类的构造函数,这时,父类后无需括号。当父类可以使用无参方式调用构造函数初始化时,super关键字可以省略,这时,默认调用父类该构造函数。

区别:

Java中,构造函数没有主次之分,所以调用任何一个父类构造函数都可以。

swift中,一个指定构造器必须调用直接父类的指定构造器,一个便利构造器只能调用当前类的其他构造器,一个便利构造器必须最终调用一个指定构造器。

kotlin中,存在主构造函数时,与swift相同,当不存在主构造函数时,次构造函数可以调用父类中任何一个构造函数。

 

在初始化子类时,会按照当前类中构造方法的调用顺序,反向依次执行。

完整的执行顺序是:父类的主构造函数---->父类的次构造函数---->子类的主构造函数---->子类的次构造函数。

l  当子类不存在主构造函数时,次构造函数可以直接调用父类的构造函数。

l  当子类存在主构造函数时,只能由主构造函数调用父类的构造函数。

3.       覆盖函数

父类中方法默认修饰符是final,如果想要可以被子类重写,需要显式open,子类中重写时,需要加override关键字,Java中不加关键字也不会报错。

如果想要禁止再次被重写,那么需要显式加上final。

构造函数不能被重写。

4.       覆盖属性

与覆盖函数类似,父类中声明为open的属性,可以在子类中使用override关键字重写。

父类中的val属性可以在子类中重写为var,但是var属性不可以被重写为val的。

5.       覆盖规则

父类与接口中,有相同名字的函数,必须要在子类中重写,在重写的方法中使用super<超类型名>.functionName()来声明调用的是哪个超类型名中的函数。

当父类中与接口中同名的方法为final修饰时,在子类中无法重写同名方法,会报错。

 

当父类与接口有同名函数,并且接口中的函数没有默认实现时:

a)        不重写同名函数,会使用从父类中继承来的函数,作为接口函数的实现。

b)        如果重写,则可以使用super. functionName ()来调用父类函数。

     

kotlin中:如果接口同名函数有默认实现,那么必须在子类重写该方法,因为该子类继承了较多的实现。在生产过程中需要尽量避免该现象的产生。

 

6.       抽象类

l  抽象类与类中的函数不需要open修饰,就可以被继承。

l  可以用一个抽象成员覆盖一个非抽象成员。

 

7.       伴生对象:关键字companion

l  可省略伴生对象的名称,系统默认使用Companion作为伴生对象名称

l  可以继承类、实现接口

l  可以通过在伴生对象中创建外部类的实例来调用外部类的函数和属性。

     

伴生对象继承了一个类的时候,那么该类的所有成员属性和成员方法,都可以用伴生对象外部类的点表达式静态调用。

 

Q:取消static而引入伴生对象的意义:

A:把所有的静态的属性和方法都集中到了一起,代码易读性高。

 

引申:单例模式的实现

a)        伴生对象   val c = AA.instance

b)        Object      val c = C

 

四、      接口

1.       接口中的函数

kotlin中接口的函数可以有默认的实现。

l  有默认实现的函数可以不在实现类中重写

l  有默认实现的函数,可以再实现类中的init块中、次构造函数中、成员函数中、给属性赋值时调用。

 

2.       接口中的属性

它可以有属性但必须声明为抽象或提供访问器实现。

l  抽象的属性:需要在实现类中重写

l  提供getter(可以加setter),可以用作计算属性

3.       解决覆盖冲突

与继承的覆盖规则相同,使用<超类型名>.functionName()。

五、      可见性修饰符

类、对象、接口、构造函数、方法、属性和它们的 setter 都可以有 可见性修饰符。 (getter 总是与属性有着相同的可见性。) 在 Kotlin 中有这四个可见性修饰符:private、 protected、 internal 和 public。 如果没有显式指定修饰符的话,默认可见性是 public。

l  如果你不指定任何可见性修饰符,默认为 public,这意味着你的声明将随处可见;

l  private用于顶层声明时,于当前文件内可见;用于类中声明时,于当前类中可见;

l  internal用于顶层声明时,它会在相同模块内随处可见;用于类中声明时,即使类的修饰符为public,用internal修饰的对象(成员属性、成员函数等),在其它模块也是不可见的;

l  protected 本类与子类中可见,不适用于顶层声明。

 

可见性修饰符作用域如下:

作用域

其他Module

当前Module

当前文件

本类

子类

public

internal

 

private

 

 

 

protected

 

 

 

 

六、      扩展

kotlin中能够扩展一个类的新功能而无需继承该类或使用像装饰者这样的任何类型的设计模式,(仅)支持扩展函数和属性。

1.       扩展是静态解析的

静态解析:在编译时,就已经确定类型。

动态解析:在编译时不确定类型,在执行时才会确认到底是什么类型(比如:多态中的继承,父类中定义抽象方法,每个子类有不同的实现)。

 

按照调用的扩展函数所定义的接收参数类型决定,该调用哪一个类的扩展(父子类)。

2.       扩展函数

成员函数:被扩展的类原有的函数

 

l  当扩展函数与成员函数同名同参时,调用函数,执行成员函数。

l  扩展函数可以重载成员函数。

l  可以给子类和父类扩展相同名称的函数。

l  不可以扩展构造函数,扩展构造函数会改变原有的类。

l  扩展函数需要有函数体。

 

3.       扩展属性

由于扩展没有实际的将成员插入类中,因此对扩展属性来说幕后字段是无效的。这就是为什么扩展属性不能有初始化器。他们的行为只能由显式提供的 getters/setters 定义(只能扩展计算属性)。

     

不能扩展类中已有的属性。

4.       其他

对伴生对象、内部类、嵌套类、接口以及Java中定义的类和接口进行扩展。

l  对伴生对象、内部类、嵌套类的扩展方式:外部类.扩展类.方法或属性,伴生对象如果省略了类名,则用Companion代替。

l  对接口扩展,与普通扩展相同,扩展函数需要有函数体。

l  对java中定义的类与接口的扩展与在kotlin中相同。

 

5.       扩展的作用域

l  大多数时候我们在顶层定义扩展,即直接在包里。

l  要使用所定义包之外的一个扩展,我们需要在调用方导入

 

6.       扩展声明为成员

分发接收者:如果一个类内部有其他类的扩展,那么它的实例就是分发接收者。

扩展接收者:被扩展的类的实例。

 

注意:

a)        在顶层定义的扩展,可以在任意可以见到当前扩展的地方使用(可见性修饰符修饰)。

b)        在一个类中给其他类进行扩展,那么这些扩展,只能在当前类或者当前类的子类中使用,不可以在类外部使用。

 

Q1:给一个类扩展两个相同名称的函数会怎样?

a)        如果在顶层扩展,将会报错,不可以有这样的写法。

b)        如果扩展声明为成员,在父类与子类中分别扩展,可以override。

Q2:扩展一个与父类成员函数相同的函数会怎样?

无效果,因为子类继承了父类的成员函数,调用时会执行子类中的成员函数。

七、      数据类

在java中的Model类是把有关系的一组数据封装在一起的类文件,通常Model类中会只定义属性和get、set方法,而且每个Model类都是一个单独的文件。kotlin中对此进行了优化,将这些只保存数据的类定义为数据类并用data关键字修饰,一个文件中可以声明多个数据类。

 

编译器自动从主构造函数中声明的所有属性导出以下成员:

l  equals()/hashCode() 对,

l  toString() 格式是 "User(name=John, age=42)",

l  componentN()函数 按声明顺序对应于所有属性(多用于解构声明),

l  copy() 函数,复制出克隆对象,可以在复制的时候,对保存的属性进行修改

 

数据类可继承类与实现接口。

可以在数据类中定义自己的方法,可扩展。

八、      密封类

相当于枚举类型的扩展,但可以比枚举类型做更多的工作。

密封类的子类只能与密封类定义在同一个文件内,但继承密封类子类的类(间接继承者)可以放在任何位置,不仅限于同文件内。

密封类不能实例化,但密封类的子类可以。

使用密封类的关键好处在于使用 when 表达式 的时候,如果能够验证语句覆盖了所有情况,就不需要为该语句再添加一个 else 子句了

 

 

 

相关文章
|
15天前
|
移动开发 Java Android开发
构建高效Android应用:探究Kotlin与Java的性能差异
【4月更文挑战第3天】在移动开发领域,性能优化一直是开发者关注的焦点。随着Kotlin的兴起,其在Android开发中的地位逐渐上升,但关于其与Java在性能方面的对比,尚无明确共识。本文通过深入分析并结合实际测试数据,探讨了Kotlin与Java在Android平台上的性能表现,揭示了在不同场景下两者的差异及其对应用性能的潜在影响,为开发者在选择编程语言时提供参考依据。
|
16天前
|
数据库 Android开发 开发者
构建高效Android应用:Kotlin协程的实践指南
【4月更文挑战第2天】随着移动应用开发的不断进步,开发者们寻求更流畅、高效的用户体验。在Android平台上,Kotlin语言凭借其简洁性和功能性赢得了开发社区的广泛支持。特别是Kotlin协程,作为一种轻量级的并发处理方案,使得异步编程变得更加简单和直观。本文将深入探讨Kotlin协程的核心概念、使用场景以及如何将其应用于Android开发中,以提高应用性能和响应能力。通过实际案例分析,我们将展示协程如何简化复杂任务,优化资源管理,并为最终用户提供更加流畅的体验。
|
25天前
|
调度 数据库 Android开发
构建高效Android应用:Kotlin协程的实践与优化
在Android开发领域,Kotlin以其简洁的语法和平台友好性成为了开发的首选语言。其中,Kotlin协程作为处理异步任务的强大工具,它通过提供轻量级的线程管理机制,使得开发者能够在不阻塞主线程的情况下执行后台任务,从而提升应用性能和用户体验。本文将深入探讨Kotlin协程的核心概念,并通过实例演示如何在实际的Android应用中有效地使用协程进行网络请求、数据库操作以及UI的流畅更新。同时,我们还将讨论协程的调试技巧和常见问题的解决方法,以帮助开发者避免常见的陷阱,构建更加健壮和高效的Android应用。
32 4
|
27天前
|
移动开发 Java Android开发
构建高效Android应用:Kotlin协程的实践之路
【2月更文挑战第31天】 在移动开发领域,性能优化和流畅的用户体验一直是开发者追求的目标。随着Kotlin语言的流行,其异步编程解决方案——协程(Coroutines),为Android应用带来了革命性的并发处理能力。本文将深入探讨Kotlin协程的核心概念、设计原理以及在Android应用中的实际应用案例,旨在帮助开发者掌握这一强大的工具,从而提升应用的性能和响应能力。
|
28天前
|
安全 Android开发 开发者
构建高效Android应用:Kotlin与协程的完美结合
【2月更文挑战第30天】在移动开发领域,性能优化和流畅的用户体验是关键。本文深入探讨了如何通过结合Kotlin语言和协程技术来提升Android应用的性能和响应能力。我们将分析Kotlin的优势,介绍协程的基本概念,并通过实际案例展示如何在应用中实现协程以简化异步编程,从而提供更加高效的解决方案。
|
28天前
|
Java 编译器 Android开发
构建高效Android应用:探究Kotlin与Java的性能差异
【2月更文挑战第30天】 随着Kotlin成为开发Android应用的首选语言,开发者社区对于其性能表现持续关注。本文通过深入分析与基准测试,探讨Kotlin与Java在Android平台上的性能差异,揭示两种语言在编译效率、运行时性能和内存消耗方面的具体表现,并提供优化建议。我们的目标是为Android开发者提供科学依据,帮助他们在项目实践中做出明智的编程语言选择。
|
28天前
|
移动开发 调度 Android开发
构建高效Android应用:探究Kotlin协程的优势与实践
【2月更文挑战第30天】 在移动开发领域,尤其是针对Android平台,性能优化和应用流畅度始终是开发者关注的重点。近年来,Kotlin语言凭借其简洁性和功能性成为Android开发的热门选择。其中,Kotlin协程作为一种轻量级的线程管理解决方案,为异步编程提供了强大支持,使得编写非阻塞性代码变得更加容易。本文将深入分析Kotlin协程的核心优势,并通过实际案例展示如何有效利用协程提升Android应用的性能和响应速度。
|
28天前
|
数据库 Android开发 开发者
构建高效Android应用:采用Kotlin协程优化网络请求处理
【2月更文挑战第30天】 在移动应用开发领域,网络请求的处理是影响用户体验的关键环节。针对Android平台,利用Kotlin协程能够极大提升异步任务处理的效率和简洁性。本文将探讨如何通过Kotlin协程优化Android应用中的网络请求处理流程,包括协程的基本概念、网络请求的异步执行以及错误处理等方面,旨在帮助开发者构建更加流畅和响应迅速的Android应用。
|
1月前
|
移动开发 Java Android开发
构建高效Android应用:Kotlin与协程的完美融合
【2月更文挑战第25天】 在移动开发领域,性能优化和应用响应性的提升是永恒的追求。随着Android Jetpack组件库的不断丰富,Kotlin语言已经成为Android开发的首选。而Kotlin协程作为一种新的并发处理方案,它以轻量级线程的形式,为开发者提供了简洁高效的异步编程手段。本文将深入探讨Kotlin协程在Android应用中的实践运用,以及如何通过这种技术改善用户界面的流畅度和后台任务的处理能力,进而构建出更高效、更稳定的Android应用。
|
17天前
|
Java Android开发 开发者
构建高效Android应用:Kotlin协程的实践与优化
在响应式编程范式日益盛行的今天,Kotlin协程作为一种轻量级的线程管理解决方案,为Android开发带来了性能和效率的双重提升。本文旨在探讨Kotlin协程的核心概念、实践方法及其在Android应用中的优化策略,帮助开发者构建更加流畅和高效的应用程序。通过深入分析协程的原理与应用场景,结合实际案例,本文将指导读者如何优雅地解决异步任务处理,避免阻塞UI线程,从而优化用户体验。