Kotlin object的几种用法示例详解
作者:无糖可乐爱好者 发布时间:2022-07-02 18:03:53
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
猜你喜欢
- 有时候需要根据条件查询得出的数据较多,需要分页显示到页面上。这时点击下一页就不方便每次带查询条件在数据库中分页。可以在list中进行分页。p
- 开发环境:IntelliJ IDEA 2019.2.2Spring Boot版本:2.1.8一、发布REST服务1、IDEA新建一个名称为r
- 1、在Android studio中进行打开一个项目的文件之后,然后进行点击Android stuio中菜单中的“tools”的选项。在弹出
- 和线程停止相关的三个方法/*中断线程。如果线程被wait(),join(),sleep()等方法阻塞,调用interrupt()会清除线程中
- 一、创建字符串创建字符串的方式有很多种,当是常见的一般就三种1. 直接赋值(常用)String str = "hello worl
- 本文实例为大家分享了spring mvc实现文件上传与下载功能的具体代码,供大家参考,具体内容如下文件上传在pom.xml中引入spring
- scheduleAtFixedRate(task,time,period)task-所要安排的任务 time-首次执行任务的时间 perio
- 本文实例展示了C#自定义函数NetxtString实现生成随机字符串的方法,在进行C#项目开发中非常实用!分享给大家供大家参考。一、生成随机
- 1 内部类概述如果一个类存在的意义就是为指定的另一个类,可以把这个类放入另一个类的内部。就是把类定义在类的内部的情况就可以形成内部类的形式。
- 不同点:不能直接实例化接口。接口不包含方法的实现。接口可以多继承,类只能单继承。类定义可以在不同的源文件之间进行拆分。相同点:接口、类和结构
- 有个网站需要生成静态页。据以往经验,凡比较烂的空间,短时间内运行耗能大的运算,都会出现“service unavailable”,以致网页无
- 前言代码生成器,也叫逆向工程,是根据数据库里的表结构,自动生成对应的实体类、映射文件和接口。看到很多小伙伴在为数据库生成实体类发愁,现分享给
- 前言最近测试给我提了一个bug,说我之前提供的一个批量复制商品的接口,产生了重复的商品数据。追查原因之后发现,这个事情没想象中简单,可以说一
- Java Function的使用一、方法介绍表示接受一个参数并产生结果的函数。参数类型 T - 函数输入的类型R - 函数的结果类型方法介绍
- 面试题1:说一下抽象类和接口有哪些区别?正经回答:抽象类和接口的主要区别:从设计层面来说,抽象类是对类的抽象,是一种模板设计;接口是行为的抽
- 本文实例为大家分享了Unity3D实现旋钮控制灯光效果的具体代码,供大家参考,具体内容如下前言实际上使用的是非常简单的方式,通过开启以及关闭
- /*冒泡排序:双层循环1.外层循环:控制排序轮数,排序数组长度减1(最后一次循环只剩下一个元素,不需要比较,同时数组已完成排序。2.内层循环
- 这篇文章主要介绍了SpringBoot下如何实现支付宝接口的使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值
- Unity没有像Vs那样的“*.sln”的项目工程文件,不能通过这个文件来打开工程。但是原有的打开已有工程的方法太过于麻烦了,则现在来通过添
- 文件上传在Web应用中非常普遍,要在Java Web环境中实现文件上传功能非常容易,因为网上已经有许多用Java开发的组件用于文件上传,本文