網站首頁 編程語言 正文
1.object:匿名內部類
在Android最常用的匿名內部類之一就是點擊事件,用Java語言寫的話就是下面這樣:
public interface OnClickListener { void onClick(View v); } button.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View v){ } });
上面的代碼在Kotlin中就要這么寫
interface OnClickListener { fun onClick(v: View) } button.setOnClickListener(object : View.OnClickListener { override fun onClick(v: View) { } })
上面的代碼中object: View.OnClickListener
就是匿名內部類的使用方式。
這里的代碼為了說明【object: 匿名內部類】的用法所以寫的比較啰嗦,如果用Lambda寫法的話可以這么寫
button.setOnClickListener { }
上面是匿名內部類常用的一種方式,還有另一種用法就是:在繼承一個抽象類的同時還可以實現多個接口,直接看代碼:
interface Behavior { fun sleep() } interface Characteristics { fun gender() } abstract class Person { abstract fun walk() } fun main() { // 這個匿名內部類,在繼承了Person類的同時,還實現了Behavior(行為)、Characteristics(特征)兩個接口 val item = object : Person(), Behavior, Characteristics { override fun sleep() { } override fun gender() { } override fun walk() { } } }
對上面的代碼說明:在繼承了Person類的同時,還實現了Behavior(行為)、Characteristics(特征)兩個接口并分別重寫了他們的方法,這種用法靈活性比較高。
2.object: 伴生對象
Kotlin 當中沒有 static 關鍵字,所以我們沒有辦法直接定義靜態方法和靜態變量。不過,Kotlin 還是為我們提供了伴生對象,來幫助實現靜態方法和變量。
先看一種嵌套類的特殊情況
class Person { object InnerSingleton { fun foo() {} } } //反編譯后的Java代碼 public final class Person { public static final class InnerSingleton { @NotNull public static final Person.InnerSingleton INSTANCE; public final void foo() { } private InnerSingleton() { } static { Person.InnerSingleton var0 = new Person.InnerSingleton(); INSTANCE = var0; } } }
由以上代碼可知定義的foo
方法并不是一個靜態方法,那要如何實現這個靜態方法呢,可以foo
方法上面加入注解@JvmStatic
,這樣反編譯后foo
方法就是一個靜態方法了
class Person { object InnerSingleton { @JvmStatic fun foo() {} } } //反編譯后的Java代碼 public final class Person { public static final class InnerSingleton { @NotNull public static final Person.InnerSingleton INSTANCE; @JvmStatic public static final void foo() { } private InnerSingleton() { } static { Person.InnerSingleton var0 = new Person.InnerSingleton(); INSTANCE = var0; } } }
經過這么改動Kotlin和Java的實現方式就都可以這么寫
Person.InnerSingleton.foo()
此時還有一個問題,foo
方法的InnerSingleton
有點多余,它并沒有實際意義,想直接調用Person.foo()
要怎么做?
在object
前面加入一個companion
關鍵字即可
//Kotlin代碼 class Person { companion object InnerSingleton { @JvmStatic fun foo() { } } } //反編譯后的代碼 public final class Person { @NotNull public static final Person.InnerSingleton InnerSingleton = new Person.InnerSingleton((DefaultConstructorMarker)null); @JvmStatic public static final void foo() { InnerSingleton.foo(); } public static final class InnerSingleton { @JvmStatic public final void foo() { } private InnerSingleton() { } // $FF: synthetic method public InnerSingleton(DefaultConstructorMarker $constructor_marker) { this(); } } }
加入companion
就實現了Person.foo()
的調用。companion object
就是Kotlin中的伴生對象, 它其實是嵌套單例的一種也是情況。
通過上面的代碼可以得出一個結論:當伴生對象存在時如果還存在
@JvmStatic
修飾的方法或者屬性,那么Kotlin編譯成Java代碼后它就會被挪到外部的類中變成靜態成員。嵌套單例是
object
單例的一種特殊情況,伴身對象是嵌套單例的一種特殊情況。
3.單例模式
Kotlin中定義一個普通的單例模式非常簡單只需要在類名前面用object
聲明即可
object Utils{ }
這樣就能實現單例,但是Java中的單例是比較復雜的,并且還有懶漢式和餓漢式之分,這樣真的就能定義嗎?
我們看一下這段代碼反編譯成Java的代碼看一看就很清楚了:
public final class Utils { @NotNull public static final Utils INSTANCE; private Utils() { } static { Utils var0 = new Utils(); INSTANCE = var0; } }
static
中的代碼由虛擬機決定只會運行一次,同時在保證線程安全的前提下也保證了INSTANCE
只運行一次。
但是這里有2個缺陷:
- 不支持懶加載
- 不能傳遞參數,在Android中會經常用到
context
的情況,不能傳參這個用法就沒有意義
那要如何解決?
- 單例模式——借助懶加載委托
class Person(val name: String) { } fun main() { val user by lazy { Person("張三") } println("user is name ${user.name}") }
上面的代碼使用了by lazy{ }
創建了Person
對象,而Person
對象最終創建的時機是在println
創建的,也就是說懶加載委托的特點就是只有在使用這個對象是才會創建實例。
- 單例模式—— 伴生對象 Double Check
直接看代碼,代碼是從《朱凱·Kotlin編程第一課》直接拷貝過來的
class UserManager private constructor(name: String) { companion object { @Volatile private var INSTANCE: UserManager? = null fun getInstance(name: String): UserManager = // 第一次判空 INSTANCE ?: synchronized(this) { // 第二次判空 INSTANCE ?: UserManager(name).also { INSTANCE = it } } } } fun main() { // 使用 UserManager.getInstance("Tom") }
這里首先定義了一個INSTANCE
并且用private修飾
這樣就可以保證不能被外界直接訪問,同時添加了@Volatile
注解可以保證INSTANCE
的可見性,而 getInstance() 方法當中的 synchronized,保證了 INSTANCE 的原子性。因此,這種方案還是線程安全的。
另外還要注意的一點是初始化定義的INSTANCE
的默認值時null
你這也就保證了只有在使用它的時候才會被實例化,也就是說實現懶加載的模式。
這中實現方式也是可以傳參的,在getInstance()
方法中定義需要的屬性即可實現傳參
這個代碼其實跟Java的懶漢模式 + synchronized 同步鎖非常相似
public class UserManager { private static volatile UserManager mInstance = null; private UserManager() { } public static UserManager getInstance() { if (mInstance == null) { synchronized (UserManager.class) { if (mInstance == null) { mInstance = new UserManager(); } } } return mInstance; } }
上面介紹了3中單例模式的實現方式,他們的使用場景如下:
- 如果單例占用內存很小并且堆內存不敏感也不需要傳參,直接使用
object
即可,例如常用且無需傳參的的Utils
類; - 如果單例占用內存很小并且不需要傳參,但是它的內部屬性會觸發消耗資源的網絡請求和數據庫查詢,這是就需要用
object
搭配by lazy{ }
; - 如果工程比較簡單,只有少數的單例場景、懶加載需求、需要傳遞參數就需要使用
Double Check
的方式了,例如PreferenceUtils
。
原文鏈接:https://juejin.cn/post/7169382354425741343
相關推薦
- 2022-07-16 訓練YOLOX時,出現“BrokenPipeError: [Errno 32] Broken pip
- 2022-08-25 C++超詳細梳理IO流操作_C 語言
- 2022-06-16 golang?validator庫參數校驗實用技巧干貨_Golang
- 2022-03-23 C語言可變長的參數列表詳解_C 語言
- 2022-07-13 Linux OS 運行python腳本中smtplib has no attribute SMTP_
- 2023-03-22 golang如何使用gomobile進行Android開發_Golang
- 2022-08-01 OpenCV連通域數量統計學習示例_python
- 2023-02-03 C++中為何推薦要把基類析構函數設置成虛函數_C 語言
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支