当前位置: 首页 > news >正文

kotlin常用语法点理解

语法点

空指针安全:可空类型修饰符

就是?,表明变量可能为null。

?及相关的elvis运算符(?:)的引入相比java是一大进步,可避免大量的NPE问题。

装箱类型?

kotlin无需装箱类型,因为它有?,比如:

val a: Int = 100; //自动用int
val b: Int? = 200;  //自动用Integer

字符串插值

不用String.format或StringBuilder,直接用${}将变量或表达式插入字符串里,很便捷:

fun getPrjWorkDir(project: Project): String {return "${project.basePath}/.backend_checker";}

当然,遇到循环里拼接字符串还是建议用StringBuilder

必须的override

java的@Override注解是可选的,而kotlin的override关键字是必须的。

对象表达式==匿名类实例

形如:

object: AbstractListener() {override fun func1() {...}override fun func2() {...}
}

等价于java里的创建匿名类实例:

new AbstractListener() {@Overridepublic void func1() {...}@Overridepublic void func2() {...}
}

特别的,对于函数式接口(SAM,只有一个抽象方法),还可以使用“接口名+lambda”的简化写法:

ignoreAllItem = MenuItem("Ignore All")
// ActionListener为SAM, 只有一个actionPerformed接口,因此可以直接在lambda里写方法体
ignoreAllItem.addActionListener {val model = table.model...}

双感叹号

强制保证引用不能为null,若为null,抛出NPE异常。个人不推荐使用,一般用于明确了绝不会为null的情况。

val+get()

等价于getter,但是作为属性而非成员函数存在,每次访问属性时都会重新计算,所以要注意使用中额外的性能开销。如果能确保一定的scope内值不变,使用时建议先用局部变量承载,避免每次都重新计算:

private fun buildNativeCmd(project: Project): String {// 这里用局部变量exePath承载,避免每次调用getterval exePath = exePathLOG.info("exe path:$exePath")if (!File(exePath).exists()) {LOG.error("exe:$exePath NOT found!")throw RuntimeException("No $EXE_NAME found!")}val fullCmd = exePath + " -d " + project.basePathLOG.warn("buildNativeCmd cmd:$fullCmd")return fullCmd}private val exePath: Stringget() = pluginRoot + File.separator + "bin" + File.separator + EXE_NAME

个人并不推荐这种写法,这会让使用者误认为属性调用是没有代价的。

static变量

kotlin没有static,等价的写法是定义在类的伴生对象里:

companion object {// 等价于 staticprivate val LOG = Logger.getInstance(AnalyzeTask::class.java)// 伴生对象里的const val等价于static finalconst val EXE_NAME: String = "XXX"}

for循环

kotlin样例:

// 循环变量i无需提前声明,直接使用
for(i in 1..10)

全局函数与全局变量

kotlin并非纯OO,类似于scala,属于“FP+OO”的多范式语言,函数像类一样,在kotlin里是第一类对象。所以,支持全局函数或全局变量并不奇怪。

另外,kotlin里也不像java那样强制“单类单文件”。

属性赋值==setter

kotlin里的属性赋值就等价于调用setter函数(如果有的话)。

类似于val+get,我们也可以对类的可变属性用var+set来实现。

List和MutableList

kotlin在类型层面严格区分“只读”和“可变”集合,List是只读,不支持add、remove,MutableList是可变。但这只是类型层面的约束,并非运行层面的保证,因MutableList也是List,所以List底层依然可能可变,不能以此作为线程安全的考量。

与java互通

kotlin能100%与java互通,所以可以采用java/kotlin并存的方式逐步改造已有代码,改造完成后,去掉一些原本用于语言互通的注解(比如@JvmStatic、@JvmRecord、 @JvmField)即可。

继承

kotlin的继承都使用冒号,语法上不区分extends和implements,但语义上“只能extends一个类,可以implements多个接口”的约束仍在。

构造函数

kotlin里,主构造函数=类声明+init块,例如:

class LintResultPanel(private val project: Project, private val errors: MutableList<LintError>) :SimpleToolWindowPanel(false, true), DataProvider {private val table = JBTable()init {setContent(ScrollPaneFactory.createScrollPane(table))table.model = ErrorsTableModel()table.setShowGrid(false)...}

上例中,第一行就是类声明,有2个属性:project和errors,在init块里做复杂的初始化工作。

次构造函数则用constructor指定,但次构造必须调用到主构造(通过this调用)。

takeIf/taskUnless/let

非集合类对象的takeIf和taskUnless 等价于 集合filter

非集合类对象的let 等价于 集合map。

见“对象链式写法”的例子。

apply和let的区别

两者都是对对象做映射处理,区别在于apply最终返回的仍是对象自身(即this),而let返回的是其函数体最后一行的值(一般肯定不是this)。因此,apply常用于链式配置,而let用于空安全或变换结果。

apply的例子:

private val table = JBTable().apply {// 这里相当于this.setRowSelectionAllowed(true)setRowSelectionAllowed(true)setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION)}

这里在创建Swing Table后,用apply设置其支持多选。用let肯定就不对了。

apply和also的区别

两者的功能很像,最终返回的都是对象自身。我们看声明:

public inline fun <T> T.also(block: (T) -> kotlin.Unit): Tpublic inline fun <T> T.apply(block: T.() -> kotlin.Unit): T

最终返回的都是T,但also是有参的(不写默认为it),而apply是无参的,且由于是T.(),说明apply紧跟的是T的无参成员函数,因此其内部用的是this(当然this一般会省略)。所以上一节的例子,如果用also改写,应为:

private val table = JBTable().also {it.setRowSelectionAllowed(true)it.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION)}

相比apply不那么直观。

因此,apply常用于对象创建后的链式配置,而also则用于其它有副作用的场景,比如日志或校验。当然,这只是习惯用法,并非绝对要求。

对象链式写法

替换if-else冗长的写法,比如:

var lineNo = err.lineNo?.let { err.lineNo - 1 } ?: 0

上述代码用到了kotlin的safe_navi+对象let映射+elvis三种能力,等价于:

        var lineNo = 0if (err.lineNo != null) {lineNo = err.lineNo - 1}

return居然是表达式

这点没想到啊,return不是语句,而是表达式,返回类型是Nothing,Nothing是任何类型的子类型,所以可以放在任何需要值的地方。比如:

override fun actionPerformed(e: AnActionEvent) {val project = e.project ?: returnAnalyzeTask(project).queue()}

return要能用在elvis表达式里,就必须是一个表达式,而非语句。所以kotlin为了语法的需要,赋予了return表达式的特质。表达式的返回值可以是任意的类型,因此return的值也必须是任意类型的子类,所以只能是Nothing。

return:非局部返回

与java不同,kotlin里的return默认是非局部返回(Non-local Return, NLR),即return会退出最近的外层函数,而非包裹它的lambda。如只想退出lambda,而非整个外层函数,得用“return+@标签”的写法,比如下面的例子:

init {...// 右键菜单val popup = JPopupMenu()val ignoreItem = JMenuItem("Ignore")popup.add(ignoreItem)...ignoreItem.addActionListener {val selected = table.selectedRowsif (selected.isEmpty()) {Messages.showInfoMessage(project,"No rows selected","Hint")// 【注意!!】这里如只用return,会跳出整个init块,而非addActionListener的lambda块。所以必须加标签。// 且return和标签之间不能有空格!return@addActionListener}val model = table.model...}...
}

init块本质上是类的主构造函数(参见“构造函数”一节),如将上述代码里的return@addActionListener改为return,会跳出主构造函数。但我们这里addActionListener后的lambda其实是一个针对函数式接口(SAM)简化的对象表达式,是一个菜单响应处理事件,所以return应该只是从lambda里退出即可。如果仍用对象表达式,因为fun的存在,不会有问题,但语法简化之后就会触发return的NLR问题。好在,编译器层面能给出告警:

return is not allowed here

Unit和Nothing

kotlin没有void,用Unit表示,Unit是一个单例(而非类型!),定义如下:

public object Unit {public open fun toString(): kotlin.String { /* compiled code */ }
}

Nothing则是一个类型(而非实例!),定义如下:

public final class Nothing private constructor() {
}

看到了吗,Nothing的构造器是私有的,意味着无法创建Nothing的实例。

Nothing主要用于表示return、throw的值。

with块

类似pascal的with,后跟receiver及指定函数块,函数块里可用省略属主的方式调用receiver的方法或属性。常用来简化代码,例如:

private fun hideColumn(table: JTable, colIndex: Int) {// 用with避免每次都要写table.columnModel.getColumn(colIndex)with(table.columnModel.getColumn(colIndex)) {minWidth = 0maxWidth = 0width = 0preferredWidth = 0}}

when

代替switch-case或多个if-else,例如:

return when (column) {0 -> e.filePath1 -> e.lineNoelse -> e.message}

注意,when也是有值的。

异常声明?

想抛就抛,kotlin里无需异常声明。

之前写java的时候就觉得所谓的受检异常用起来很费劲,有时候干脆用RuntimeException来规避异常声明。kotlin完全废弃了这一套繁琐的东西。

资源自动释放

kotlin里对应try-with-resource的写法是use:

StringWriter().use { sw ->PrintWriter(sw).use {e.printStackTrace(it)log.info(sw.toString())}}
http://www.dtcms.com/a/570243.html

相关文章:

  • STM32是什么?
  • 提高网站的访问速度网站后缀net
  • 安卓网站开发c 网站开发 书
  • 网站编辑 图片批量免费素材网站无版权
  • 给网站网站做优化重庆网站编辑职业学校
  • 【双机位A卷】华为OD笔试之【排序】双机位A-银行插队【Py/Java/C++/C/JS/Go六种语言】【欧弟算法】全网注释最详细分类最全的华子OD真题题解
  • 巴彦淖尔网站建设公司互联网服务公司有哪些
  • 国家建设部网站倪虹旅游公司的网站怎么做
  • 医院做网站备案都需要什么网站判断手机跳转代码
  • 荆门网站建设514885网站如何添加内容
  • 电子商务的网站建设分析建设银行东莞招聘网站
  • 杭州大型网站建设彩票网站建设 极云
  • Spring-cloud 主键loadbalance
  • 网页模板下载网站知乎教育机构网站建设方案书
  • 高端网站建设浩森宇特电气行业网站建设多少钱
  • php网站开发考试定制版app
  • 得到Class类的方法及所有类型的Class对象
  • 网站keywords中企动力做网站行吗
  • 友情链接中有个网站域名过期了会影响wordpress站内链接
  • 虚幻引擎5 GAS开发俯视角RPG游戏 P07-04 输入配置数据资产
  • 网站建设需要注意哪些内容电商o2o是什么意思
  • 反向传播在GAN训练中的作用
  • 架构进阶——解读50页大型集团企业IT基础架构应用运维体系解决方案【附全文阅读】
  • 电子商务网站建设编码广州微信网站建设平台
  • HTML炫酷烟花⑨
  • 网站评论怎么做的想做一个网站平台怎么做的
  • 网站模板制作与安装教程公司注册后每年的费用
  • 【java基础语法】------方法
  • 网站登不了企业建设网银wordpress自己安装了插件吗
  • C++ 实现大数加法