iis服务器助手广告广告
返回顶部
首页 > 资讯 > 移动开发 >kotlinobject关键字单例模式实现示例详解
  • 933
分享到

kotlinobject关键字单例模式实现示例详解

kotlinobject关键字单例模式kotlinobject 2023-01-12 12:01:04 933人浏览 八月长安
摘要

目录正文一、 匿名内部类二、单例模式三、伴生对象1、深入分析伴生对象2、用伴生对象实现工厂模式3、用伴生对象实现单例模式(1)、借助懒加载委托(2)、伴生对象 Double Chec

正文

object 关键字有三种不同的语义:匿名内部类、伴生对象、单例模式。因为 Kotlin 的设计者认为,这三种语义本质上都是在定义一个类的同时还创建了对象。在这样的情况下,与其分别定义三种不同的关键字,还不如将它们统一成 object 关键字。

一、 匿名内部类

Android中用java写View的点击事件:

findViewById(R.id.tv).setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        //do something
    }
});

在 Kotlin 当中,我们会使用 object 关键字来创建匿名内部类。同样,在它的内部,我们也必须要实现它内部未实现的方法。这种方式不仅可以用于创建接口的匿名内部类,也可以创建抽象类的匿名内部类:

findViewById<TextView>(R.id.tv).setOnClickListener(object : View.OnClickListener {
    override fun onClick(v: View?) {
        //do something
    }
})
//上面的代码可以用SAM转换简化,IDE会提示

Java 和 Kotlin 相同的地方就在于,它们的接口与抽象类,都不能直接创建实例。想要创建接口和抽象类的实例,我们必须通过匿名内部类的方式。

在 Kotlin 中,匿名内部类还有一个特殊之处,就是我们在使用 object 定义匿名内部类的时候,其实还可以在继承一个抽象类的同时,来实现多个接口:

//抽象类和抽象方法
 abstract class Person{
     abstract  fun isAdult()
}
//接口
interface AListener {
    fun getA()
}
//接口
interface BListener {
    fun getB()
}
//继承一个抽象类的同时,来实现多个接口
private val  item = object :Person(),AListener,BListener{
    override fun isAdult() {
        //do something
    }
    override fun getA() {
        //do something
    }
    override fun getB() {
        //do something
    }
}

在日常的开发工作当中,我们有时会遇到这种情况:我们需要继承某个类,同时还要实现某些接口,为了达到这个目的,我们不得不定义一个内部类,然后给它取个名字。但这样的类,往往只会被用一次就再也没有其他作用了。所以针对这种情况,使用 object 的这种语法就正好合适。我们既不用再定义内部类,也不用想着该怎么给这个类取名字,因为用过一次后就不用再管了。

引申:可以把函数当做参数简化定义接口的操作。以前写java时应该都写过很多如下的接口回调:

class DownloadFile {
    //携带token下载文件
    fun downloadFile(token:String) {
        val filePath = ""
        listener?.onSuccess(filePath)
    }
    //定义成员变量
    private var listener: OnDownloadResultListener? = null
    //写set方法
    fun setOnDownloadResultListener(listener: OnDownloadResultListener){
        this.listener = listener
    }
    //定义接口
    interface OnDownloadResultListener {
        fun onSuccess(filePath:String)
    }
}

通过函数当做参数就不需要定义接口了:

class DownloadFile {
    private var onSuccess: ((String?) -&gt; Unit)? = null
    fun downloadFile(token:String) {
        val filePath = ""
        onSuccess?.invoke(filePath)
    }
    fun setOnDownloadResultListener(method:((String?) -&gt; Unit)? = null){
        this.onSuccess = method
    }
}
//调用
DownloadFile().downloadFile("")
DownloadFile().setOnDownloadResultListener { filePath -&gt;
    print("$filePath")
}

二、单例模式

在 Kotlin 当中,要实现单例模式其实非常简单,我们直接用 object 修饰类即可:

object StringUtils {
    fun getLength(text: String?): Int = text?.length ?: 0
}
//反编译
public final class StringUtils {
   @NotNull
   public static final StringUtils INSTANCE;  //静态单例对象
   public final int getLength(@Nullable String text) {
      return text != null ? text.length() : 0;
   }
   private StringUtils() {
   }
   static {  //静态代码块
      StringUtils var0 = new StringUtils();
      INSTANCE = var0;
   }
}

这种方式定义的单例模式,虽然简洁,但存在两个缺点:

1、不支持懒加载。

2、不支持传参构造单例。写构造方法会报错,会提示object修饰的类不允许有构造方法。

三、伴生对象

1、深入分析伴生对象

Kotlin 当中没有 static 关键字,所以我们没有办法直接定义静态方法和静态变量。不过,Kotlin 还是为我们提供了伴生对象,来帮助实现静态方法和变量。

我们先来看看 object 定义单例的一种特殊情况,看看它是如何演变成“伴生对象”的:

class User() {
    object InnerClass {
        fun foo() {}
    }
}

用object修饰嵌套类,看下反编译的结果:

public final class User {
   //object修饰的内部类为静态内部类
   public static final class Inner {
      @NotNull
      public static final User.Inner INSTANCE;  //静态单例对象
      public final void foo() {
      }
      private Inner() {
      }
      //通过static静态代码块创建了单例对象
      static {
         User.Inner var0 = new User.Inner();
         INSTANCE = var0;
      }
   }
}

调用的时候的代码

User.InnerClass.foo()

可以看到foo方法并不是静态方法,那加上@JVMStatic这个注解试试:

class User() {
    object InnerClass {
        @JvmStatic
        fun foo() {}
    }
}
//反编译结果
public final class User {
   public static final class InnerClass {
      @NotNull
      public static final User.InnerClass INSTANCE;
      @JvmStatic
      public static final void foo() {   //foo方法变成了静态方法
      }
      private InnerClass() {
      }
      static {
         User.InnerClass var0 = new User.InnerClass();
         INSTANCE = var0;
      }
   }
}

foo方法变成了一个静态方法,但是在使用的时候还是要User.InnerClass.foo(),而User类中的静态方法应该是直接User.foo()调用才对,这还是不符合定义静态方法的初衷。那在 Kotlin 如何实现这样的静态方法呢?我们只需要在前面例子当中的 object 关键字前面,加一个 compaNIOn 关键字即可。

①不加@JvmStatic注解

//假如不加@JvmStatic注解
class User() {
   companion object InnerClass {
        fun foo() {}
    }
}
//反编译
public final class User {
   @NotNull
   public static final User.InnerClass InnerClass = new User.InnerClass((DefaultConstructORMarker)null);
   public static final class InnerClass {
      public final void foo() {
      }
      private InnerClass() {
      }
      // $FF: synthetic method
      public InnerClass(DefaultConstructorMarker $constructor_marker) {
         this();
      }
   }
}
//调用
User.foo()
//反编译调用的代码
User.InnerClass.foo();

如果不加上@JvmStatic注解调用的时候只是省略了前面的单例对象InnerClassfoo仍然不是User的静态方法。

②加@JvmStatic注解

//假如加@JvmStatic注解
class User() {
   companion object InnerClass {
       @JvmStatic
        fun foo() {}
    }
}
//反编译
public final class User {
   @NotNull
   public static final User.InnerClass InnerClass = new User.InnerClass((DefaultConstructorMarker)null);
   @JvmStatic
   public static final void foo() {  //多生成了一个foo方法,但其实还是调用的下面的foo方法
      InnerClass.foo();
   }
   public static final class InnerClass {
      @JvmStatic
      public final void foo() {   //实际的foo方法
      }
      private InnerClass() {
      }
      // $FF: synthetic method
      public InnerClass(DefaultConstructorMarker $constructor_marker) {
         this();
      }
   }
}

可以看到这个时候多生成了一个静态的foo方法,可以通过User.foo()真正去调用了,而不是省略掉了InnerClass单例对象(把InnerClass对象放在了静态方法的实现中)。

那又有问题来了,上面二种方式应该如何选择,哪种情况下哪个好,什么时候该加注解什么时候不该加注解?

解析:1、用companion修饰的对象会创建一个Companion的实例:

class User {
   companion object {
        fun foo() {}
    }
}
//反编译
public final class User {
   @NotNull
   public static final User.Companion Companion = new User.Companion((DefaultConstructorMarker)null);
   public static final class Companion {
      public final void foo() {
      }
      private Companion() {
      }
      // $FF: synthetic method
      public Companion(DefaultConstructorMarker $constructor_marker) {
         this();
      }
   }
}
//java中调用
User.Companion.foo();

如果不加@JvmStatic,java调用kotlin代码会多创建这个Companion实例,会多一部分内存开销,所以如果这个静态方法java需要调用,那务必要把@JvmStatic加上。

2、多创建一个静态foo方法会不会多内存开销? 答案是不会,因为这个静态的foo方法调用的也是Companion中的方法foo方法,所以不会有多的内存开销。

2、用伴生对象实现工厂模式

所谓的工厂模式,就是指当我们想要统一管理一个类的创建时,我们可以将这个类的构造函数声明成 private,然后用工厂模式来暴露一个统一的方法,以供外部使用。Kotlin 的伴生对象非常符合这样的使用场景:

// 私有的构造函数,外部无法调用
class User private constructor(name: String) {
    companion object {
     @JvmStatic
         fun create(name: String): User? {
            // 统一检查,比如敏感词过滤
            return User(name)
        }
    }
}

3、用伴生对象实现单例模式

(1)、借助懒加载委托

class MainActivity : AppCompatActivity() {
    //借助懒加载委托实现单例
    private val people by lazy { People("张三", 18) }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
}
//反编译后
public final class MainActivity extends AppCompatActivity {
   private final Lazy people$delegate;
   private final People getPeople() {
      Lazy var1 = this.people$delegate;
      Object var3 = null;
      return (People)var1.getValue();
   }
   protected void onCreate(@Nullable Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      this.setContentView(1300000);
   }
   public MainActivity() {   //构造方法
      this.people$delegate = LazyKt.lazy((Function0)null.INSTANCE);  //lazy方法有线程安全的实现
   }
}

MainActivity的构造方法中通过LazyKt.lazy获取类的代理对象,看下LazyKt.lazy源码实现:


public actual fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)

public actual fun <T> lazy(mode: LazyThreadSafetyMode, initializer: () -> T): Lazy<T> =
    when (mode) {
        LazyThreadSafetyMode.SYNCHRONIZED -> SynchronizedLazyImpl(initializer)
        LazyThreadSafetyMode.PUBLICATION -> SafePublicationLazyImpl(initializer)
        LazyThreadSafetyMode.NONE -> UnsafeLazyImpl(initializer)
    }

(2)、伴生对象 Double Check

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 }
            }
     }
}
// 使用
UserManager.getInstance("Tom")

我们定义了一个伴生对象,然后在它的内部,定义了一个 INSTANCE,它是 private的,这样就保证了它无法直接被外部访问。同时它还被注解“@Volatile”修饰了,这可以保证INSTANCE的可见性,而getInstance()方法当中的synchronized,保证了INSTANCE的原子性。因此,这种方案还是线程安全的。

同时,我们也能注意到,初始化情况下,INSTANCE 是等于 null 的。这也就意味着,只有在getInstance() 方法被使用的情况下,我们才会真正去加载用户数据。这样,我们就实现了整个UserManager的懒加载,而不是它内部的某个参数的懒加载。

另外,由于我们可以在调用getInstance(name) 方法的时候传入初始化参数,因此,这种方案也是支持传参的。

单例模式最多的写法,注意如果参数是上下文,不能传递ActivityFragment的上下文,不然会有内存泄漏。(单例的内存泄漏)

(3)、抽象类模板

如果有多个类似于上面的单例,那么就会有很多重复代码,于是尝试抽象成模板代码:

//要实现单例类,就只需要继承这个 BaseSingleton 即可
//P为参数,T为返回值
abstract class BaseSingleton<in P, out T> {
    @Volatile
    private var instance: T? = null
    //抽象方法,需要我们在具体的单例子类当中实现此方法
    protected abstract fun creator(param: P): T
    fun getInstance(param: P): T =
        instance ?: synchronized(this) {
            instance ?: creator(param).also { instance = it }
        }
}

通过伴生对象实现抽象类,并给出具体实现

//构建UploadFileManager对象需要一个带参数的构造方法
class UploadFileManager(val param: String) {
    //伴生对象实现BaseSingleton抽象类
    companion object : BaseSingleton<String, UploadFileManager>() {
        //重写方法并给出具体实现
        override fun creator(param: String): UploadFileManager {
            return UploadFileManager(param)
        }
    }
    fun foo(){
        print("foo")
    }
}
//调用
UploadFileManager.getInstance("张三").foo()

因为构造方法的限制这种封装也有一定的局限性。

以上就是kotlin object关键字单例模式实现示例详解的详细内容,更多关于kotlin object关键字单例模式的资料请关注编程网其它相关文章!

--结束END--

本文标题: kotlinobject关键字单例模式实现示例详解

本文链接: https://www.lsjlt.com/news/177493.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • kotlinobject关键字单例模式实现示例详解
    目录正文一、 匿名内部类二、单例模式三、伴生对象1、深入分析伴生对象2、用伴生对象实现工厂模式3、用伴生对象实现单例模式(1)、借助懒加载委托(2)、伴生对象 Double Chec...
    99+
    2023-01-12
    kotlin object关键字单例模式 kotlin object
  • Go语言单例模式示例详解
    目录简单单例模式加锁的单例模式双check 的单例模式sync.Once 的单例模式简单单例模式 单例模式是创建类型的模式,它是为了保证执行期间内只有一个实例。使用 Golang 指...
    99+
    2022-11-11
  • this关键字和单例模式是什么
    这篇文章主要讲解了“this关键字和单例模式是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“this关键字和单例模式是什么”吧!一、this关键字1.假...
    99+
    2022-10-19
  • Java设计模式之单例模式示例详解
    目录0.概述1.饿汉式1.1 饿汉式单例实现1.2 破坏单例的几种情况1.3 预防单例的破坏2.枚举饿汉式2.1 枚举单例实现2.2 破坏单例3.懒汉式4.双检锁懒汉式5.内部类懒汉...
    99+
    2022-11-12
  • Python单例模式实例详解
    本文实例讲述了Python单例模式。分享给大家供大家参考,具体如下: 单例模式:保证一个类仅有一个实例,并提供一个访问他的全局访问点。 实现某个类只有一个实例的途径: 1,让一个全局变量使得一个对象被访问,...
    99+
    2022-06-04
    详解 实例 模式
  • Java Web关键字填空示例详解
    (1)在TestServletRequest中将名为“param”,值为“HelloWorld”的信息存入request范围内,并利用...
    99+
    2022-11-13
  • java 单例模式的实例详解
    java 单例模式的实例详解概念:    java中单例模式是一种常见的设计模式,单例模式分三种:懒汉式单例、饿汉式单例、登记式单例三种。    单例模式有一下特点:   1、单例类只能有一个实例。   2、单例类必须自己自己创建自己的唯一...
    99+
    2023-05-31
    java 单例模式 ava
  • java 单例模式和工厂模式实例详解
    单例模式根据实例化对象时机的不同分为两种:一种是饿汉式单例,一种是懒汉式单例。私有的构造方法指向自己实例的私有静态引用以自己实例为返回值的静态的公有的方法饿汉式单例 public class Singleton { private ...
    99+
    2023-05-31
    java 单例模式 工厂模式
  • Verilog语言关键字模块例化实例讲解
    目录关键字:例化,generate,全加器,层次访问命名端口连接顺序端口连接端口连接规则用 generate 进行模块例化层次访问关键字:例化,generate,全加器,层次访问 ...
    99+
    2023-05-15
    Verilog语言关键字模块例化 Verilog 模块例化
  • Java中Volatile关键字详解及代码示例
    一、基本概念先补充一下概念:Java内存模型中的可见性、原子性和有序性。可见性:可见性是一种复杂的属性,因为可见性中的错误总是会违背我们的直觉。通常,我们无法确保执行读操作的线程能适时地看到其他线程写入的值,有时甚至是根本不可能的事情。为了...
    99+
    2023-05-30
    java volatile关键字 ava
  • Java注释和关键字实例详解
    目录Java注释单行注释多行注释文档注释Java关键字结束语Java注释 注释的含义:当我们写程序时需要对代码进行解释说明,这时我们就需要使用注释,以便于后期我们对之前敲过的代码还会...
    99+
    2023-01-15
    java注释规范 java中多行注释 java有效关键字
  • Kotlinthis关键字的使用实例详解
    目录this可以用来访问类的成员使用this访问类实例二级构造函数的委托在 Kotlin 中,this 关键字允许我们引用一个类的实例,该类的函数恰好正在运行。此外,还有其他方式可以...
    99+
    2023-02-17
    Kotlin this关键字 Kotlin this的使用
  • java中的This关键字和单例模式是什么
    这篇文章主要介绍“java中的This关键字和单例模式是什么”,在日常操作中,相信很多人在java中的This关键字和单例模式是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解...
    99+
    2022-10-19
  • MySQL 中MATCH 全文搜索关键字示例详解
    MATCH()函数是mysql中专门用于全文搜索的函数。该函数的作用是在一个文本列上执行全文搜索,并且返回一个匹配度的得分。MATCH()函数可以接收一个或多个搜索词,可以支持Boolean、Natural Langua...
    99+
    2023-09-26
    mysql match全文搜索 mysql match关键字
  • C语言学习之关键字的示例详解
    目录1. 前言2. 什么是关键字3. extern-声明外部符号4. auto-自动5. typedef-类型重定义(类型重命名)6. register-寄存器6.1 存储器6.2 ...
    99+
    2022-11-13
    C语言 关键字
  • java设计模式-单例模式实现方法详解
    目录饿汉式静态变量静态代码块懒汉式线程不安全线程安全双重检查静态内部类总结单例模式,属于创建类型的一种常用的软件设计模式。通过单例模式的方法创建的类在当前进程中只有一个实例(根据需要...
    99+
    2022-11-12
  • Java中的final关键字详解及实例
    Java中的final关键字1、修饰类的成员变量 这是final的主要用途之一,和C/C++的const,即该成员被修饰为常量,意味着不可修改。 上面的代码对age进行初始化后就不可再次赋值,否则编译时会报类似上图的错误。 如果修...
    99+
    2023-05-31
    java final 关键字
  • go sync.Once实现高效单例模式详解
    目录1. 简介2. 基本实现2.1 单例模式定义2.2 sync.Once实现单例模式2.3 其他方式实现单例模式2.3.1 全局变量定义时赋值,实现单例模式2.3.2 init 函...
    99+
    2023-03-14
    go sync.Once单例模式 go sync.Once
  • Java之单例模式实现方案详解
      单例模式是最常用到的设计模式之一,熟悉设计模式的朋友对单例模式都不会陌生。一般介绍单例模式的书籍都会提到 饿汉式 和 懒汉式 这两种实现方式。但是除了这两种方式,本文还会介绍其他...
    99+
    2022-11-12
  • Python实现单例模式的四种方式详解
    简介:单例模式可以保证一个类仅有一个实例,并提供一个访问它的全局访问点。适用性于当类只能有一个实例而且客户可以从一个众所周知的访问点访问它,例如访问数据库、MQ等。 实现方式: 1、...
    99+
    2022-11-11
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作