kotlin(3):类和对象以及其他的概念属性

xiaoxiao2021-02-28  71

类和对象

类声明由类名,类头(指定其类型参数,主构造函数等)和由大括号包围的类体构成,类头和类体都是可选的,如果一个类没有类体,可以省略花括号.

 

1.主构造函数

kotlin中的一个类可以有一个主构造函数和多个次构造函数,主构造函数是类头的一部分,跟在类名后.

class Demo constructor(data: String){}

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

class Demo(data: String){}

主构造函数不能包含任何的代码,初始化的代码可以放到以init关键字作为前缀的初始化块中

class Demo(data: String){ init{ print(“init”) } }

主构造函数的参数可以在初始化块中使用,它们也可以在类体内声明的属性初始化器中使用

class Demo(var data: String){}

与普通属性一样,主构造函数中声明的属性可以是可变的var或者是只读的val

如果构造函数有注解或者可见性修饰符,这个constructor关键字是必需的,并且这些修饰符必须在它的前面

 

2.次构造函数

类也可以声明前缀有constructor的次构造函数

class Demo { var data:Int = 0//若是以这样的形式声明成员变量必须赋予初值 constructor(data:Int) {//可以有多个不同参数的次构造函数 } }

如果一个非抽象类没有声明任何(主或次)构造函数,它会有一个生成的不带任何参数的主构造函数,构造函数的可见性是public,可以声明一个带有非默认可见性的空的主构造函数

class Demo private constructor(){}

:jvm,如果主构造函数的所有的参数都有默认值,编译器会生辰给一个额外的无参构造函数,它将使用默认值

class Demo(var data:Int = 1) {} var demo = Demo()//demo的成员变量的data的值为1

3.创建类的实例

Kotlin没有new,这点是与java最不同的地方

var demo = Demo()//可变的对象 var val demo = Demo(1);//不可变的对象 val

4.类成员

一个类可以包含:构造函数,初始化块,函数,属性,嵌套类和内部类,对象声明

5.继承

kotlin中所有的类都有一个共同的超类Any(类似于java中的Object),这对于没有超类型声明的类是默认超类

不过Any中的成员很少,只有equals() hashCode() toString()

 

要声明一个显式的超类型,把类型放到类头的冒号之后:

open class Base()//可以定义为open或者abstract中的一个 class Demo():Base(){}

如果该类有一个主构造函数,其基类必须用基类的主构造函数参数就地初始化

如果类没有主构造函数,那么每个次构造函数必须用super关键字初始化其基类,或委托给另一个构造函数做到这一点,注意:在这种情况下,不同的次构造函数可以调用基类型的不同的构造函数

class MyView : View { constructor(ctx: Context) : super(ctx) constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs) }

类上的open标注与javafinal相反,它允许其他类从这个类继承,默认情况下,kotlin中所有的类都是final

 

6.覆盖方法

Kotlin需要显式标注可覆盖的成员和覆盖后的成员

open class Base{ open fun v(){} } class Demo():Base(){ Override fun v(){} }

Demo类中的v()函数上必须加上override标注,如果没写,编译器会报错.如果函数没有标注open则子类中不允许定义相同签名的函数,不论加不加override.在一个final类中,开放成员是禁止的,相反在标记为override的成员本事是开放的,也就是说,它可以在子类中覆盖,如果想禁止再次覆盖,可以使用final关键字修饰,这样就禁止被再次覆盖了

open class Demo():Base(){ Final override fun v(){} }

7.覆盖属性

属性覆盖与方法覆盖类似,在超类中声明然后在派生类中重新声明的属性必须以override开头,并且它们必须具有兼容的类型,每个声明的属性可以由具有初始化器的属性或者具有getter()方法的属性覆盖

open class Base { open var data:Int = 1 get() {return 2} } class Demo : Base(){ override var data:Int = 3 get() = 4 }

最后的Demodata的值为4

可以用一个var覆盖基类的val,但反之不行.

 

8.覆盖规则

kotlin,实现继承由下述规则规定:如果一个类从它的直接超类继承相同成员的多个实现,它必须覆盖这个成员并提供其自己的实现(也许用继承来的其中之一).为了表示采用从哪个超类型继承的实现,我们使用由尖括号中超类型名限定的super,super<Base>

open class Base { open fun f(){} } interface Base2{ fun f(){} } class Demo : Base(),Base2{ override fun f(){ super<Base2>.f() super<Base>.f() } }

 

 

9.抽象类

类和其中的某些成员可以声明为abstract,抽象成员在本类中可以不用实现,需要注意的是,我们并不需要用open标注一个抽象类或者函数,,因为是废话..

 

 

可以用一个抽象成员覆盖基类的费抽象的开放成员

10.伴生对象

java不同,kotlin中类没有静态方法,在多数情况下,它建议简单地使用包级函数

如果需要写一个可以无需用一个类的实例来调用,但需要访问类内部的函数,也就是对java中的static的需要.kotlin中可以写成该类内对象声明中的一员,中文意思就是:如果在类内声明一个伴生对象,就可以像在java中调用static相同的语法来调用其成员,只使用类名作为限定符.

如下:

open class Demo { companion object { fun f(){ } } }

注意:每一个类中只能有一个companion object 此时的对象名称是Companion

也可以自定义名字:

open class Demo { companion object MyCompanion{ fun f(){ } } }

调用的时候:

 

Demo.f() Demo.MyCompanion.f()

 

11.单例模式(对象声明)

kotlin中单例实现很简单,直接贴代码:

object Single{ fun f(){ println("single") } }

注意:对象声明不能在局部作用域中,但是它们可以嵌套到其他对象声明或非内部类中

 

对象表达式和对象声明的区别:

A.对象表达式是在使用它们的地方立即执行,创建对象

B.对象声明是在第一次被访问到时延迟初始化的,懒汉式的单例模式

C.伴生对象的初始化是在相应的类被加载时,java静态初始化的寓意

 

12.属性和字段

声明属性:

Kotlin的类可以有属性,属性可以用var声明为可变的,否则使用只读关键字val

 

class Demo{ var data1:String = “” var data2:Int = 0 val data3:Int = 1 }

要使用一个属性,名称引用即可

GettersSetters

声明一个属性的完整语法是

var <propertyName> [:<propertyType>] [=<property_initializer>] [<getter>] [<setter>]

初始器gettersetter是可选的,属性类型如果可以从初始器(或者从getter返回值)中推断出来,也可以省略

EG:

var demo1:Int? //错误:需要显示地初始化,隐含默认getter和setter var demo2 = 1 //类型Int,默认getter和setter

一个只读属性的语法和一个可变的属性的语法有两方面的不同:1.只读属性的用val开始代替var, 2.只读属性不允许setter

val demo:Int?//类型 Int, 默认getter,必须在构造函数中初始化 val demo2 = 1//类型Int 默认getter

自定义gettersetter的例子

var demo:String get() = this.toString() set(value) { This.demo = value }

改变一个访问器的可见性或者对其注解,但是不需要改变默认的实现,可以定义访问器而不定义其实现

var demo:String=”hello” private set//此setter是私有的并且有默认的实现 Var demo2:Any? = null @Inject set //用Inject注解此setter

幕后字段:

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

var counter = 0 //此初始化值直接写到幕后字段里面 set(value) { If(value > 0) filed = value }

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

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

例如下面的情况,就没有幕后字段:

val isEmpty : Boolean get() = this.size == 0

编译期常量

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

A.位于顶层或者是object的一个成员

B.String或原生类型值初始化

C.没有自定义getter

这些属性可以用在注解中:

const val demo:String=”hello world”

惰性初始化属性

一般的,属性声明为非空类型必须在构造函数中初始化,然而这经常不方便,例如:属性可以通过依赖注入来初始化,或者在单元测试的setup方法中初始化,这种情况下,不可以在构造函数中提供一个非空初始器,这时候可以用lateinit修饰符标记该属性

lateinit var demo:String

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

在初始化前访问一个lateinit属性会抛出一个特定异常,该异常明确标识该属性被访问及它没有初始化的事实

 

委托属性

最常见的一类属性就是简单地从幕后字段中读取,另一方面,使用自定义gettersetter可以实现属性的任何行为,介于两者之间,属性如何工作有一些常见的模式

 

转载请注明原文地址: https://www.6miu.com/read-47134.html

最新回复(0)