一起来学Kotlin:概念:4. Kotlin 函数注解:Suppress,Volatile, Synchronized, Bindable, RequiresApi,SerializedName

这篇博客我们解释 Kotlin 函数注解:Suppress,Volatile, Synchronized, Bindable, RequiresApi,SerializedName 等。


文章目录

  • 一起来学Kotlin:概念:4. Kotlin 函数注解:Suppress,Volatile, Synchronized, Bindable, RequiresApi,SerializedName
    • 1. Deprecated
    • 2. Suppress
    • 3. Volatile
    • 4. Synchronized
    • 5. Bindable
    • 6. RequiresApi
    • 7. SerializedName
    • Reference

1. Deprecated

如果需要废弃一个方法,只需要在方法钱加上 @Deprecated 即可。

Kotlin 对于 @Deprecated 的定义:

/*** Marks the annotated declaration as deprecated.** A deprecated API element is not recommended to use, typically because it's being phased out or a better alternative exists.** To help removing deprecated API gradually, the property [level] could be used.* Usually a gradual phase-out goes through the "warning", then "error", then "hidden" or "removed" stages:* - First and by default, [DeprecationLevel.WARNING] is used to notify API consumers, but not to break their compilation or runtime usages.* - Then, some time later the deprecation level is raised to [DeprecationLevel.ERROR], so that no new Kotlin code can be compiled*   using the deprecated API.* - Finally, the API is either removed entirely, or hidden ([DeprecationLevel.HIDDEN]) from code,* so its usages look like unresolved references, while the API remains in the compiled code* preserving binary compatibility with previously compiled code.** @property message The message explaining the deprecation and recommending an alternative API to use.* @property replaceWith If present, specifies a code fragment which should be used as a replacement for*  the deprecated API usage.* @property level Specifies how the deprecated element usages are reported in code.*  See the [DeprecationLevel] enum for the possible values.*/
@Target(CLASS, FUNCTION, PROPERTY, ANNOTATION_CLASS, CONSTRUCTOR, PROPERTY_SETTER, PROPERTY_GETTER, TYPEALIAS)
@MustBeDocumented
public annotation class Deprecated(val message: String,val replaceWith: ReplaceWith = ReplaceWith(""),val level: DeprecationLevel = DeprecationLevel.WARNING
)

所以说,Deprecated的输入有三个,一个是message,解释弃用并建议使用替代 API 的信息;第二个是 replaceWith,指定可用于替换已弃用的函数,属性或类的代码片段;而 level 则指定如何在代码中报告已弃用的元素用法(WARNING, ERROR,HIDDEN三个选项)。后两个参数是有预设值的,第一个参数没有。所以,如果需要使用Deprecated函数注解的话,例如下面代码:

@Deprecated("xxx")
fun testKt(){}

replaceWith的一个例子:

2. Suppress

如果需要消除一些编译时的警告,可以使用 @Suppress("xxx")

比如:

class MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)@Suppress("UNUSED VARIABLE")val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this,R.layout.activity_main)}
}

又比如:

fun unChecked(){val list: List<Any> = emptyList()@Suppress("UNCHECKED_CAST")list as List<String>
}

3. Volatile

为了强制变量中的更改立即对其他线程可见,我们可以使用注解 @Volatile,在以下示例中为:

@Volatile var shutdownRequested = false

这将保证在值更改后其他线程的更改可见性。 这意味着如果线程 X 修改了 shutdownRequested 的值,线程 Y 将能够立即看到更改。

@Volatile 的官方解释是:

Marks the JVM backing field of the annotated property as volatile, meaning that writes to this field are immediately made visible to other threads.

4. Synchronized

总结:Java 有 synchronized 关键字,可以将其应用于方法以确保一次只有一个线程可以访问它们。进入同步方法的线程获得锁(被锁定的对象是包含类的实例),并且在释放锁之前没有其他线程可以进入该方法。Kotlin通过 @Synchronized 注解提供了相同的功能。

在多线程的世界中,我们需要跨线程访问共享对象,如果我们不同步我们的工作,就会发生不希望的情况。比如下面例子:

import kotlinx.coroutines.*
fun main() = runBlocking {var sharedCounter = 0val scope = CoroutineScope(newFixedThreadPoolContext(4, "synchronizationPool")) // We want our code to run on 4 threadsscope.launch {val coroutines = 1.rangeTo(1000).map { //create 1000 coroutines (light-weight threads).launch {for(i in 1..1000){ // and in each of them, increment the sharedCounter 1000 times.sharedCounter++}}}coroutines.forEach {corotuine->corotuine.join() // wait for all coroutines to finish their jobs.}}.join()println("The number of shared counter should be 1000000, but actually is $sharedCounter")
}

在上面的例子中,我们在 4 个线程上启动了 1000 个协程,并且每个协程将 sharedCounter 递增 1000 倍,所以 sharedCounter 的最终值应该是 1000000。但我们运行上述代码后会发现,并不会输出这个值。

在我们从技术上解释这里发生了什么之前,假设有一个思考室(柜台),很多人(线程)想要使用它,但一次只允许一个人。 这个房间有一扇门,当房间被占用时,它是关闭的。 在这种情况下发生的情况是,当一个人在房间内时,其他人也可以打开门,进来使用房间。 但是门需要锁!

在上面的代码中,为了增加 sharedCounter,每个线程都尝试执行以下操作以在内部增加 sharedCounter 值:

  • 获取其当前值,
  • 将其存储在临时变量中,并将临时变量加 1,
  • 将临时变量保存到 sharedCounter。

但是,如果一个线程获取当前值,并且由于我们处于多线程世界中,另一个线程跳入并尝试获取当前值怎么办?他们都将获得相同的价值!因此,它们中的每一个都将该值增加 1,并存储相同的值。

这个问题可能以类似的其他方式发生,例如,一个线程超过了第二个级别,但在存储它之前,其他线程递增并保存 sharedCounter 值,当第一个线程跳转到它的第三步时,它保存了一个旧的 sharedCounter 的版本。这就是为什么最终值不是我们所期望的。

如果我们使用 Synchronized,代码如下:

import kotlinx.coroutines.*
var sharedCounter = 0
@Synchronized fun updateCounter(){sharedCounter++
}
fun main() = runBlocking {val scope = CoroutineScope(newFixedThreadPoolContext(4, "synchronizationPool")) // We want our code to run on 4 threadsscope.launch {val coroutines = 1.rangeTo(1000).map { //create 1000 coroutines (light-weight threads).launch {for(i in 1..1000){ // and in each of them, increment the sharedCounter 1000 times.updateCounter() // call the newly created function that is now synchronized}}}coroutines.forEach {corotuine->corotuine.join() // wait for all coroutines to finish their jobs.}}.join()println("The number of shared counter is $sharedCounter")
}

如我们所见,输出值始终是正确的。 在这个场景中,Synchronized 类似于门上的锁,只有一把钥匙,人们需要用它来开门和锁门。 因此,当一个人(一个线程)进入房间使用时,除非该人离开房间并归还钥匙,否则其他人都无法进入。

其他例子:

@Database(entities = [Note::class], version = 1)
abstract class NoteDatabase : RoomDatabase() {abstract fun noteDao(): NoteDaocompanion object {private var instance: NoteDatabase? = null@Synchronizedfun getInstance(ctx: Context): NoteDatabase {if(instance == null)instance = Room.databaseBuilder(ctx.applicationContext, NoteDatabase::class.java,"note_database").fallbackToDestructiveMigration().addCallback(roomCallback).build()return instance!!}private val roomCallback = object : Callback() {override fun onCreate(db: SupportSQLiteDatabase) {super.onCreate(db)populateDatabase(instance!!)}}private fun populateDatabase(db: NoteDatabase) {val noteDao = db.noteDao()subscribeOnBackground {noteDao.insert(Note("title 1", "desc 1", 1))noteDao.insert(Note("title 2", "desc 2", 2))noteDao.insert(Note("title 3", "desc 3", 3))}}}
}

5. Bindable

数据绑定有两种与数据交互的基本方式:BaseObservable 类及其关联的 @Bindable 注释,以及 LiveData 可观察包装器(LiveData observable wrapper)。

如果您从 BaseObservable 继承您的 ViewModel,那么您注释为 @Bindable 的所有变量都将添加到 BR 全局引用对象中。 BR 是可绑定资源(Bindable Resource)。 与您的 R 引用对象类似,这只是引用特定视图的静态整数的集合。

比如,我们在build.gradle(:app)中,我们先设置dataBinding:

buildFeatures{dataBinding = true
}

fragment_login.xml中,我们设置了需要需要binding的ViewModel:

<data><variablename="myLoginViewModel"type="com.example.mydatabaseapp.login.LoginViewModel" />
</data>

我们在LoginFragment.kt

loginViewModel = ViewModelProvider(this, factory).get(LoginViewModel::class.java)
binding.myLoginViewModel = loginViewModel

最后,我们在 LoginViewModel.kt 中使用 @bindable

@Bindable
val inputUsername = MutableLiveData<String>()@Bindable
val inputPassword = MutableLiveData<String>()

6. RequiresApi

在我们写代码的时候,我们可能会碰到一个问题,需要调用一些之前版本的API,比如下面这个例子:

val dateTime = LocalDateTime.now()
val currentTime = dateTime.format(DateTimeFormatter.ofPattern("M/d/y H:m:ss"))

如果我们不加 @RequiresApi(Build.VERSION_CODES.O) 的话,会出现下面这类报错:

Call requires API level 26 (current min is 21): java.time.LocalDateTime#now

所以,我们需要调用API 26,Android 8 (Oreo),即,在函数的上一行加上 @RequiresApi(Build.VERSION_CODES.O) 即可。

Android API与Android版本对应关系

7. SerializedName

我们通常使用@SerializedName批注来映射JSON字段。比如下面的代码:

data class Country(// 我们使用@SerializedName批注来映射JSON字段,// name是JSON字段的,我们将其映射为countryName,用于我们这个项目@SerializedName("name")val countryName: String?,@SerializedName("capital")val capital: String?,@SerializedName("flagPNG")val flag: String?
)

我们读取的JSON文件长这样:

[{"alpha2Code": "AF","alpha3Code": "AFG","altSpellings": ["AF","Af\u0121\u0101nist\u0101n"],"area": 652230,"borders": ["IRN","PAK","TKM","UZB","TJK","CHN"],"callingCodes": ["93"],"capital": "Kabul","currencies": [{"code": "AFN","name": "Afghan afghani","symbol": "\u060b"}],"demonym": "Afghan","flagPNG": "https://raw.githubusercontent.com/DevTides/countries/master/afg.png","gini": 27.8,"languages": [{"iso639_1": "ps","iso639_2": "pus","name": "Pashto","nativeName": "\u067e\u069a\u062a\u0648"},{"iso639_1": "uz","iso639_2": "uzb","name": "Uzbek","nativeName": "O\u02bbzbek"},{"iso639_1": "tk","iso639_2": "tuk","name": "Turkmen","nativeName": "T\u00fcrkmen"}],...
]

我们希望将JSON文件中的namecapitalflagPNG的值分别提取出来,然后放到我们自己的数据类 Country 里,所以我们使用@SerializedName来将name映射到countryNamecapital映射到capitalflagPNG映射到flag

另外一个例子,比如我们这个json数据长这样:

//json1
{"class":"1""public":"2"
}//json2
{"a":"1""b":"2"
}

如 json1 中数据我们如果建立一个相同字段名的实体类显然是不可行的,因为class和public都是java/kotlin语言中的关键字,@SerializedName注解就起到作用了。

public class Json1{@SerializedName("class")private String cla;@SerializedName("public") private String pub;}

Reference

  • Kotlin之Deprecated和Suppress注解使用
  • Kotlin Suppress 的非常规用法
  • How to use volatile in Kotlin
  • @Volatile
  • Volatile Properties in Kotlin
  • Synchronization, Thread-Safety and Locking Techniques in Java and Kotlin
  • Synchronized methods
  • Reducing Data Binding Boilerplate With Kotlin
  • @TargetApi和@RequiresApi含义
  • Build.VERSION.SDK_INT >= Build.VERSION_CODES.O 何意
  • Gson中@SerializedName 注解使用
  • @SerializedName注解的简单使用
  • kotlin - 使用@field :SerializedName annotation instead of @SerializedName?的目的是什么

Kotlin 4. Kotlin 函数注解:Suppress,Volatile, Synchronized, Bindable, RequiresApi,SerializedName相关推荐

  1. Kotlin学习之函数

    函数声明 在kotlin中用关键字fun声明函数: fun double(x:Int):Int{ } 其中Int是返回值类型,x指明参数类型是为Int 函数用法 通过传统方法调用函数: val res ...

  2. 学习Kotlin(七)反射和注解

    推荐阅读: 学习Kotlin(一)为什么使用Kotlin 学习Kotlin(二)基本语法 学习Kotlin(三)类和接口 学习Kotlin(四)对象与泛型 学习Kotlin(五)函数与Lambda表达 ...

  3. 【Kotlin】Kotlin 领域特定语言 DSL 原理 一 ( DSL 简介 | 函数 / 属性扩展 )

    文章目录 I . DSL 简介 II . Kotlin 函数扩展 III . Kotlin 属性扩展 I . DSL 简介 1 . DSL ( Domain Specific Language ) 领 ...

  4. kotlin内联函数let、with、run、apply、also

    最近面试被问到kotlin内联函数,这里作个简单的总结 let 在函数体内访问该对象中的属性或方法 iv_back_activity_clock.let {it.adjustViewBounds = ...

  5. Kotlin入门(9)函数的基本用法

    上一篇文章介绍了Kotlin新增的空安全机制,控制语句部分可算是讲完了,接下来将连续描述Kotlin如何定义和调用函数,本篇文章先介绍函数的基本用法. 前面几篇文章介绍控制语句之时,在setOnCli ...

  6. kotlin内联函数_Kotlin内联函数,参数化

    kotlin内联函数 In this tutorial, we'll be looking into Kotlin inline function. We'll follow that with Re ...

  7. kotlin的入口函数

    java入口方法 package com.kushanmao.ja;/*** Created by kushanmao on 2017/7/16.*/ public class MainMethod ...

  8. 【Kotlin】Kotlin 函数总结 ( 具名函数 | 匿名函数 | Lambda 表达式 | 闭包 | 内联函数 | 函数引用 )

    文章目录 一.函数头声明 二.函数参数 1.默认参数值 2.具名参数 三.Unit 函数 四.TODO 函数抛出异常返回 Nothing 类型 五.反引号函数名 六.匿名函数 七.匿名函数的函数类型 ...

  9. 【Kotlin】Kotlin的高阶函数与Lambda表达式

    前言 Kotlin的高阶函数与Lambda表达式是Kotlin的两大特色,使用频率非常高.熟悉它的用法与本质对于简化代码.提升开发效率非常有帮助. 这两个概念不是同一个东西,但是又有非常紧密的关联.这 ...

最新文章

  1. Redis 笔记(03)— string类型(设置key、获取key、设置过期时间、批量设置获取key、对key进行加减、对key值进行追加、获取value子串)
  2. Sencha-概念-Layouts(布局)(官网文档翻译8)
  3. 保护linux系统调用,Linux软中断与系统调用
  4. 学习招投标相关知识-编写标书
  5. Linux快速入门打开你的学习之道
  6. Qt的简单介绍和安装
  7. TCP/IP / 三次握手之状态转换图和原因
  8. 我的世界java手机版怎么调按键_如何在10秒内,让我的世界立即“自爆”?一个隐藏的mc快捷键...
  9. [JS-BOM]BOM概念与组成
  10. python matplotlib模块教程_Python中的Matplotlib模块入门教程
  11. (转)淘淘商城系列——maven工程debug调试
  12. XStream将XML转换为JAVA对象快速指南
  13. 小提琴1234567位置图解_小提琴入门指法(小提琴1234567位置图解)
  14. linux tomcat 部署 JCO,JCO连接SAP时tomcat报错
  15. Ubuntu下编译vtk(java版本)【超详细-带过程截图】
  16. 高尔顿钉板 matlab,高尔顿钉板试验模拟
  17. win7怎么修改计算机皮肤,鼠标指针怎么换?小编教你win7系统更换鼠标指针皮肤的方法...
  18. 统计学三种相关系数【pearson、spearman、kendall】
  19. 电脑D盘格式化了怎么恢复
  20. 揭秘:雷电模拟器工作方式

热门文章

  1. 2014年发生的一些事情
  2. 平面设计10大手法,设计师必看
  3. 前端CSS实现渐变效果
  4. 用开源博客系统OneBlog来搭建自己的个人技术博客网站(java版博客系统)
  5. STW43NM60ND意法车规MOS管\原装现货ASEMI代理
  6. intellij idea如何在当前工作空间下打开新项目
  7. 如何设置数据库最大连接数
  8. 什么是神经元网络控制?,神经网络控制结构包括
  9. XDOJ 172-构造表达式
  10. Unity第三人称控制实现方式