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

类和对象

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

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可以实现属性的任何行为,介于两者之间,属性如何工作有一些常见的模式

猜你喜欢

转载自blog.csdn.net/weixin_37699212/article/details/72900233