91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

淺談kotlin中object關鍵字

發布時間:2020-07-20 14:25:28 來源:億速云 閱讀:544 作者:小豬 欄目:移動開發

小編這次要給大家分享的是淺談kotlin中object關鍵字,文章內容豐富,感興趣的小伙伴可以來了解一下,希望大家閱讀完這篇文章之后能夠有所收獲。

前言

object是Kotlin中的一個重要的關鍵字,也是Java中沒有的。object主要有以下三種使用場景:

  • 對象聲明(Object Declaration)
  • 伴生對象(Companion Object)
  • 對象表達式(Object Expression)
     

下面就一一介紹它們所表示的含義、用法以及注意點,保證你在看完本篇之后就可以完全掌握object關鍵字的用法。

1. 對象聲明(Object Declaration)

語法含義:將類的聲明和定義該類的單例對象結合在一起(即通過object就實現了單例模式)

基本示例

object RepositoryManager{
 fun method(){
  println("I'm in object declaration")
 }
}

即將class關鍵字替換為object關鍵字,來聲明一個類,與此同時也聲明它的一個對象。只要編寫這么多代碼,這個類就已經是單例的了。

使用

a. 在Kotlin中:

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

像在Java中調用靜態方法(在kotlin中沒有靜態方法)一樣去調用其中定義的方法,但實際上是使用RepositoryManager類的單例對象去調用實例方法。如果對此還不能理解,可以看看下面對在Java中去使用的說明。

b. 在Java中:

public class JavaTest {
 public static void main(String[] args) {
  RepositoryManager.INSTANCE.method();
 }
}

換句話說,object declaration的類最終被編譯成:一個類擁有一個靜態成員來持有對自己的引用,并且這個靜態成員的名稱為INSTANCE,當然這個INSTANCE是單例的,故這里可以這么去使用。如果用Java代碼來聲明這個RepositoryManager的話,可以有如下代碼:

class RepositoryManager{
 private RepositoryManager(){}
 public static final RepositoryManager INSTANCE = new RepositoryManager();

}

4) 注意點:

盡管和普通類的聲明一樣,可以包含屬性、方法、初始化代碼塊以及可以繼承其他類或者實現某個接口,但是它不能包含構造器(包括主構造器以及次級構造器)
它也可以定義在一個類的內部:

 class ObjectOuter {
  object Inner{
   fun method(){
    println("I'm in inner class")
   }
  }
 }
 fun main(args: Array<String>) {
  ObjectOuter.Inner.method()
 }

2、伴生對象(Companion object)

在闡述伴生對象之前,首先我們要明確一點:在Kotlin中是沒有static關鍵字的,也就是意味著沒有了靜態方法和靜態成員。那么在kotlin中如果要想表示這種概念,取而代之的是包級別函數(package-level function)和我們這里提到的伴生對象。至于它們之間的區別,不急,我們后面再說。

語法形式:

class A{
 companion object 伴生對象名(可以省略){
  //define method and field here
 }
}

基本示例:

class ObjectTest {

 companion object MyObjec{

  val a = 20

  fun method() {
   println("I'm in companion object")
  }
 }
}

使用:

a. 在Kotlin中:

fun main(args: Array<String>) {
 //方式一
 ObjectTest.MyObject.method()
 println(ObjectTest.MyObject.a)

 //方式二(推薦方式)
 ObjectTest.method()
 println(ObjectTest.a)
}

在這里請注意:在定義(定義時如果省略了伴生對象名,那么編譯器會為其提供默認的名字Companion)和調用時伴生對象名是可以省略的。而且在方式二中,注意調用形式,是通過類名.方法名()的形式進行的,我們在沒有生成ObjectTest類的對象時,調用了定義其內部伴生對象中定義的屬性和方法,是不是類似Java中的靜態方法的概念。

通過現象看本質

通過javap命令,讓我們看看其生成的字節碼:

注意紅框中,這個MyObject成員變量的類型,是使用

淺談kotlin中object關鍵字

MyObject這個內部類。注意它這里并沒有外部類的引用,說明是以靜態內部類的形式存在的。

還記得我們在前面遺留的問題:同樣都可以用來替代Java中的static的概念,那么在伴生對象中定義的方法和包級別函數有什么區別呢?

先來反編譯一個包含包級別函數的kt文件(或者說是類):

可以看出,一個名叫ObjectTest2.kt文件,實際上最終會生成一個名叫ObjectTest2Kt的類,而在這個kt文件中定義的頂級函數(包級別函數)是作為這個類的靜態方法的形態存在的。

那么現在可以回答遺留的問題了:實際上就是平級類(姑且稱之)中的靜態方法和靜態內部類中的方法的區別,因為靜態內部類中的方法是可以訪問外部類中定義的static方法和成員的,哪怕是private的(包括私有構造器,我們常用的基于靜態內部類實現的單例模式就是基于這一點),而平級類中方法是訪問不到當前類中靜態的private成員的。如果你覺得文字這么描述還不夠直觀,那么我們來看下面這一張圖(盜自Kotlin in action):

@JvmStatic注解:我們把前面定義的method方法上加上此注解,重新build工程,然后再來反編譯ObjectTest和ObjectTest$MyObject這個兩個類,看會有什么變化。

對于這個靜態內部類而言,加與不加@JvmStatic注解其類的結構是沒有變化的。但是對于目標類而言,很明顯多了一個靜態方法,這樣我們就不難理解@JvmStatic注解的作用了:將伴生對象類中定義的實例方法和屬性,添加到目標類中,并且以靜態的形式存在。

對于伴生對象,最后再補充一點:一個類的伴生對象只能有一個。仔細想想也很好理解,伴生對象的名稱是可以省略的。如果允許對應多個伴生對象,那么我們在多個伴生對象中都定義了一模一樣的函數,在調用時到底是使用哪個伴生對象的方法呢?就會產生歧義,這樣就不難理解這條語法規定了。

3、對象表達式(Object Expression)

Java的匿名內部類回顧:

在去學習對象表達式之前,我們先來回顧一下Java中的匿名內部類。

interface Contents {
 void absMethod();
}
public class Hello {

 public Contents contents() {
  return new Contents() {
   
   @Override
   public void absMethod() {
    System.out.println("method invoked...");
   }
  };
 }

 public static void main(String[] args) {

  Hello hello = new Hello();
  hello.contents().absMethod(); //打印method invoked...
 }
}
``` new Contents()

這個contents()方法返回的是一個匿名內部類的對象,這個匿名內部類實現了Contents接口。這些代碼很熟悉,不多說了。現在提出兩個局限性問題:

a. 如果在匿名內部類中新添加了一些屬性和方法,那么在外界是無法調用的

return new Contents() {
private int i = 1;
  public int value() {
   return i;
  }

  @Override
  public void absMethod() {
   System.out.println("method invoked...");
  }
 };

public static void main(String[] args) {
 Hello hello = new Hello();
 hello.contents().absMethod();
 hello.contents().value(); //Cannot resolve method 'value()'
}

當你想使用這個value方法時,編譯器會報錯。也好理解,就是多態的知識,父類型的引用是無法知曉子類添加方法的存在的。

b. 一個匿名內部類肯定是實現了一個接口或者是繼承一個類,并且只能是一個,用數學術語說是“有且只有一個”

2) 語法形式:

object [ : 接口1,接口2,類型1, 類型2]{}    //中括號中的可省略

3) 使用示例:

a. 實現一個接口或類

interface AA {
fun a()
}
fun main(args: Array) {
val aa = object : AA {
 override fun a() {
  println("a invoked")
 }
}

aa.a()
}

b. 不實現任何接口和類,并且在匿名內部類中添加方法

fun main(args: Array) {
val obj = object {
 fun a() {
  println("a invoked")
 }
}

obj.a() //打印:a invoked

}

從這個例子可以看出,前面我們提到的Java匿名內部類的第一個局限的地方在Kotlin中就不存在了,新添加的方法也是可以調用的

c. 實現多個接口和類

fun main(args: Array) {
val cc = object : AA, BB() {
override fun a() {
 }

 override fun b() {

 }

}

cc.a()
cc.b()
}

從這個例子可以看出,前面我們提到的Java匿名內部類的第二個局限性在kotlin中也不存在

4) 使用注意點:

這是Kotlin官方文檔上的一段話:匿名對象只有定義成局部變量和private成員變量時,才能體現它的真實類型。如果你是將匿名對象作為public函數的返回值或者是public屬性時,你只能將它看做是它的父類,當然你不指定任何類型時就當做Any看待。這時,你在匿名對象中添加的屬性和方法是不能夠被訪問的。

再來舉個例子幫助大家理解:

class MyTest {
private val foo = object {
 fun method() {
  println("private")
 }
}

val foo2 = object {
 fun method() {
  println("public")
 }
}

fun m() = object {
 fun method(){
  println("method")
 }
}

fun invoke(){

 val local = object {
  fun method(){
   println("local")
  }
 }

 local.method() //編譯通過
 foo.method() //編譯通過
 foo2.method() //編譯通不過
 m().method() //編譯通不過
}
}

5) 關于在匿名對象中訪問同一作用下定義的局部變量的問題:

在Java中,如果在匿名內部類中訪問外部定義的局部變量,那么該局部變量必須使用final關鍵字進行修飾,至于為什么大家可以看我之前的一篇博文。而在Kotlin中,這條限制沒有了,看下面的例子:

var a = 1
val obj = object {
fun method() {
a++
}
}
obj.method()
println(a) //打印出2

再來解釋一下:在Java中,實際上在method方法中使用的a實際上是局部變量a的一份拷貝,而不是它本身。而在Kotlin最終也是要編譯成字節碼供JVM去執行,所以本質上它是不會違背這一點的。那么它是怎么處理的呢?

當你訪問的局部變量是val時,那么也是很Java一樣,持有的是一份拷貝;而當你是一個可變變量(var)時,它的值是被存儲在Ref這個類的實例成員中,Ref變量是final的,而他其中的成員變量是可以改變的。反編譯后是可以看到Ref的身影的。

這里還有段有點意思的代碼,給大家貼出:

fun tryToCountButtonClicks(button: Button): Int{
var clicks = 0

button.setOnClickListener{
 clicks++
}

return clicks
}

button是個按鈕,這段代碼的本意上是想要統計Button被點擊的次數。但是這個函數的返回值始終是0,哪怕你點擊再多次。因為你對局部變量clicks值得修改是異步的,而此函數的返回值是在執行時就確定了的,就是你的值還沒有被修改,函數已經返回了。如果真的想統計點擊次數,可以將clicks定義成類的成員變量。

4. 對比object declaration、Companion object以及object expression的初始化時機:

a. object declaration:當第一次訪問它時才初始化,是一種懶初始化

b. Companion object:當它對應的類被加載后,它才初始化,類似Java中的靜態代碼塊

c. object expression:一旦它被執行,立馬初始化

至此,關于Kotlin中的object關鍵字的使用就介紹完了,希望大家能有所收獲~

看完這篇關于淺談kotlin中object關鍵字的文章,如果覺得文章內容寫得不錯的話,可以把它分享出去給更多人看到。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

江源县| 平潭县| 湖口县| 隆德县| 霞浦县| 闸北区| 万全县| 盘山县| 渝北区| 凉城县| 溆浦县| 沛县| 平顶山市| 应用必备| 麟游县| 镶黄旗| 十堰市| 上思县| 勐海县| 翁牛特旗| 兖州市| 布尔津县| 延寿县| 盱眙县| 宁化县| 客服| 石阡县| 大英县| 普宁市| 凤冈县| 岳池县| 新乡市| 扎鲁特旗| 克拉玛依市| 武威市| 日喀则市| 武胜县| 凌海市| 仁怀市| 民乐县| 阿鲁科尔沁旗|