Kotlin POJO 101(2)


object / companion object

object是Kotlin中的一个重要的关键字。

java 全局静态类 static class GlobalUse
kotlin没有static关键字,但有object关键字。

object声明对象

使用object可以在kotlin中实现单例模式。也可以理解为全局静态类。

object OutObject {
    var outParameter = "out"
    fun outFunction(parameter:String) {
        println("Out object function result: $parameter.")
    }
}

fun changeParam(parameter: String){
    OutObject.outParameter = parameter
}

fun main() {
    print(OutObject.outParameter)
    OutObject.outFunction("Test object");
    print("==========================\n")
    changeParam("change param")
    print(OutObject.outParameter)
}

object declaration的类最终被编译成:一个类拥有一个静态成员来持有对自己的引用,并且这个静态成员的名称为INSTANCE

注:和普通类的声明一样,可以包含属性(props)、方法(methods)、初始化代码块(init)以及可以继承其他类或者实现某个接口(:),但**不能包含构造器(constructor)**。

object 也可以用于一个内部类的声明。与外部声明无区别,也属于内部类的单例模式。当然,内部类还有其他一些玩法,我们之后再进行了解(inner)

class ObjectOuter {
    object Inner{
        fun method(){
            println("I'm in inner class")
        }
    }
}

fun main(args: Array<String>) {
    ObjectOuter.Inner.method()
}

companion object 伴生对象

因为在kotlin中没有static关键字的,就没有静态方法和静态成员。
因此在kotlin中使用包级别函数(package-level function)和伴生对象。

简单说一下包级别函数:

package com.demo

fun sayHello(name: String) {
    println("Hello $name!")
}

// import method
import com.demo.sayHello
fun main(args: Array<String>) {
    sayHello("world")
}

如果采用伴生对象实现静态方法,就需要使用companion object

示例如下:

class ObjectTest {
   // 定义时如果省略了伴生对象名,那么编译器会为其提供默认的名字Companion
  	companion object {
    //companion object MyObjec{
        val a = 20
        fun method() {
            println("I'm in companion object")
        }
    }
}

fun main(args: Array<String>) {
   // 如此调用,则与调用静态方法一致。
    ObjectTest.method()
    println(ObjectTest.a)
}

静态方法调用成员变量,要求成员变量必须是静态的

实际上,object可以用于对象表达式来声明匿名类。但需要注意:

  • 匿名对象只有定义成局部变量和private成员变量时,才能体现它的真实类型(才能被访问)。
  • 如果你是将匿名对象作为public函数的返回值或者是public属性时,你只能将它看做是它的父类,当然你不指定任何类型时就当做Any看待。这时,你在匿名对象中添加的属性和方法是不能够被访问的

未曾使用过,不做具体分析,若有需求,可自行查看。

?

Kotlin’s type system is aimed at eliminating the danger of null references from code, also known as the The Billion Dollar Mistake.

目的就在于淘汰空指针(NPE)情况。

?含义

因此,在声明类型时,自动类型推导可以区分可空(null)和不可空(non-null)。

在变量声明后加 ? 可允许变量为null。

比如:

var strA: String = "abc"
strA = null

// error : Null can not be a value of a non-null type String

若是声明可为null的变量,可使用 ?

var strB: String? = "ab"
strB = null

// correct.

但是,因为使用了? 会引发一些问题。比如无法安全调用对象的方法与属性。即当对象为null时,其不可能去调用属性方法。

strA.length // correct

strB.length // incorrent. error: variable 'b' can be null

as 作为类型转换关键字,?as可结合使用。
as?表示若可以转换为指定类型则转换,若不行,返回null。

val str = "adf"
val math = str as Int // ClassCastException

val math = str as? Int // return null

?.

因此,当你声明了可空变量,就需要采取一些方法来规避上述问题。
也许第一念头法是使用条件判断去处理null和非null的情况。但是,若用Conditions会使程序更冗长。

写到这里,突然发现,Java的Optional与其有异曲同工之妙。

因此,kotlin提供了一种safe calls的运算法 ?. 用于对可空变量的调用。

strB?.length

其实, ?. 在链式调用中优势最明显,若其中有null存在,结果为null。不需要多层判断。(Js也有?. 用于避免相同情况)

var name: String? = company?.unit?.manager?.firstName

补充一点关于 Scope functions的知识。具体了解可参考此文

?.let结合使用,将为null的对象全部过滤并执行let内的代码。类似的还有run,apply等。

val listWithNulls: List<Int?> = listOf(1,2,3,null)
for (item in listWithNulls) {
    item?.let{
         val count = it + 1 // it 关键字,替代对象
         print(count)
    }
}
// 如此便将null值过滤出let执行块内。

?:

当使用可空声明?之后,如何对null情况进行抉择呢?如果使用if else这类条件判断会显得代码过于冗长。kotlin提供?:运算符解决此类问题。
?:左边表达为null,则会执行其右边表达式,否则,返回左边表达式的值。

val strC: String? = "ccc"
val stcD: String? = null
val lenC = strC?.length ?: -1 // return 3
val lenD = strD?.length ?: -1 // return -1

val lenD = if (strD!=null) strD.length else -1 

注意:右边也可以是throw语句。

!!

有时候如果需要将nullable变量转换为non-nullable变量,则需要用上!!强制类型转换符。
同时也要注意,一旦变量被声明为non-nullable类型,若出现为null情况,就会出现NPE。
因此,不要随意使用!!

这部分内容先到此为止,若有后续需求,会继续更新。江湖路远,不见不散。


文章作者: Dragon
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Dragon !
  目录