Kotlin與Java比較:對象

前言

Kotlin作為JVM系的語言,起源于Java又不同于Java。通過在語言層面比較兩者的區(qū)別,可以使得開發(fā)者能夠快速學習,融會貫通。

匿名對象

有時需要對某個類做輕微的改動并獲取它的對象,Kotlin與Java提供了不同的支持。

  • Java
    在Java中提供了匿名內(nèi)部類對這一需求的支持,即在初始化類的地方覆寫基類的實現(xiàn)。
class Person
{
    public void show()
    {
        System.out.println(“Person”);
    }
}

class Main{
    public void test(new Person(){    //匿名內(nèi)部類
          @override
          show(){
              //xxx
          }
    });
}

匿名內(nèi)部類首先是內(nèi)部類,是在類中定義的類,同時匿名表示沒有名字,直接覆蓋其實現(xiàn)后new出來的。匿名內(nèi)部類的使用非常廣泛,特別是那種需要修改某一個類的實現(xiàn),且只需要這個實現(xiàn)的一個對象的時候。需要注意的是,在Java中,匿名內(nèi)部類只能訪問外部類的final變量。

Kotlin

在Kotlin中與匿名內(nèi)部類對應(yīng)的是對象表達式,即通過object關(guān)鍵字去定義與初始化匿名類。其寫法結(jié)構(gòu)為:

object : 基類名(主構(gòu)造函數(shù)參數(shù)){
      覆蓋類的實現(xiàn)
}

例如:

window.addMouseListener(object : MouseAdapter() {
    override fun mouseClicked(e: MouseEvent) { …… }
    override fun mouseEntered(e: MouseEvent) { …… }
})

由于object后僅有基類的名稱,故覆蓋后的類是沒有名字的,即匿名的。

若有多個基類,可以通過逗號分隔:

open class A(x: Int) {
    public open val y: Int = x
}
interface B { …… }

val ab: A = object : A(1), B {
    override val y = 15
}

若僅僅需要一個簡單對象,無基類,則可以這樣寫:

fun foo() {
    val adHoc = object {
        var x: Int = 0
        var y: Int = 0
    }
    print(adHoc.x + adHoc.y)
}

該匿名對象可以作為私有作用域的對象,也可以作為公有作用域的對象。前者返回的是匿名對象類型,后者返回的是匿名對象聲明的基類,若無基類則返回Any類型。

class C {
    // 私有函數(shù),所以其返回類型是匿名對象類型
    private fun foo() = object {
        val x: String = "x"
    }

    // 公有函數(shù),所以其返回類型是 Any
    fun publicFoo() = object {
        val x: String = "x"
    }

    fun bar() {
        val x1 = foo().x        // 沒問題
        val x2 = publicFoo().x  // 錯誤:未能解析的引用“x”
    }
}

單例對象與靜態(tài)方法

  • Java
    單例即一個類的唯一實例,在Java中為了獲得單例常會使用設(shè)計模式中的到單例模式來獲得單例。
// 一種單例的實現(xiàn)
public class SingletonDemo {
    private static SingletonDemo instance;
    private SingletonDemo(){

    }
    public static SingletonDemo getInstance(){
        if(instance==null){
            instance=new SingletonDemo();
        }
        return instance;
    }
}

與單例類似的為靜態(tài)類,但它們也有區(qū)別:

  • 單例可以延遲加載或控制,而靜態(tài)類在被第一次初始化時加載

  • 單例可以被override,而靜態(tài)類不可以

  • 單例易于被測試

  • 單例與靜態(tài)類的回收時機不同(待補充)

  • Kotlin
    在Kotlin中為了使用單例或類似于靜態(tài)類,可以使用對象聲明。對象聲明相比于對象表達式,在寫法上主要是在object后添加了類名,并且不能將該聲明放在局部作用域中,但是它們可以嵌套到其他對象聲明或非內(nèi)部類中。其例子為:

object DataProviderManager {
    fun registerDataProvider(provider: DataProvider) {
        // ……
    }
}

// 使用該對象,直接通過類名使用
DataProviderManager.registerDataProvider(……)

若對象聲明也有基類,在其名稱后添加冒號與基類即可:

object DefaultListener : MouseAdapter() {
    override fun mouseClicked(e: MouseEvent) { …… }

    override fun mouseEntered(e: MouseEvent) { …… }
}

對于Kotlin的這一設(shè)計,是通過對象聲明的名稱來對方法進行調(diào)用,與Java的靜態(tài)成員相比,還是略有不同。因為Java是通過外部的類名訪問靜態(tài)成員,而Kotlin的對象聲明是通過聲明的類名來訪問成員。為此,Kotlin還有一個新的概念,即伴生對象。

class NumberTest {
    companion object Obj {  
        var flag = false
        fun plus(x:int, y:int): Int {... }
    }
}

通過在類內(nèi)部的對象聲明前添加companion關(guān)鍵字,即為伴生對象。這個對象是屬于外部類的,通過外部類的類名即可調(diào)用該對象的成員:

NumberTest.plus(1, 2)    
NumberTest.flag

這就與Java中使用通過類名訪問靜態(tài)成員類似。

對于有名稱的伴生對象,可以通過外部類名訪問,訪問語法為:

//外部類類名.半生對象名.方法
NumberTest.Obj.plus(1, 2)   
//外部類類名.半生對象名.成員
NumberTest.Obj.flag

對于無名稱的伴生對象,可以通過外部類名訪問,訪問語法為:

class NumberTest {
    companion object Obj {  
        var flag = false
        fun plus(x:int, y:int): Int {... }
    }
}

//外部類類名.Companion.方法
NumberTest.Companion.plus(1, 2)   
//外部類類名.Companion名.成員
NumberTest.Companion.flag

注意,伴生對象成員形如Java類中的靜態(tài)成員,但實際上在運行時它們是真實對象的成員

對象表達式/對象聲明/伴生對象的區(qū)別

對象表達式和對象聲明之間有一個重要的語義差別:

  • 對象表達式是在使用他們的地方立即執(zhí)行(及初始化)的;
  • 對象聲明是在第一次被訪問到時延遲初始化的;
  • 伴生對象的初始化是在相應(yīng)的類被加載(解析)時,與 Java 靜態(tài)初始化器的語義相匹配。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容