(后续更新见原文:https://blog.iyatt.com/?p=12060)
准备入 Android,抽空了解一下 Kotlin 语法。
JDK 17.0.9
Kotlin 1.9.20
环境配置:https://blog.iyatt.com/?p=12040
https://book.kotlincn.net/text/coding-conventions.html
这个编码规范可以作为参考,当然我个人而言还是喜欢沿用自己的方案。主要来自 C/C++ 的习惯风格,比如大括号独立成行,条件、循环必须加大括号(即使只有一个语句)。不过还是遇到一个特殊的,Kotlin 不建议使用下划线命名法,在 IDEA 中编辑的时候,只要用了就会出现波浪号。这个和目前的风格有点冲突还不可调和,我最开始学编程的时候是用驼峰法,后面感觉驼峰法太紧密了,不够清晰,之后就改用下划线法了,结果 Kotlin 不让用下划线法(看到波浪线太难受了,还是得妥协)。
Kotlin 中没有 switch,但是有 when,其作用类似
fun main()
{
val value = 10
when (value)
{
1 ->
{
println("结果为 1")
}
5 ->
{
println("结果为 5")
}
10 ->
{
println("结果为 10")
}
else ->
{
println("其它")
}
}
}
Kotlin 中条件控制语句可以有返回值,这个在以前接触的语言里没见过。最终的返回值来自于匹配的代码块里最后一个语句,且接收返回值的时候,必须存在 else 以保证在没有匹配到的情况下有默认值。
Kotlin 没有类似 C/C++/Java(后有提到就简称 C 系) 的三元运算符,但是可以通过 if-else 做到三元运算符的效果,还可以提供更多 else if 条件的判断。Python 中的三元运算符语法上是用的 if-else,但是那是专用的,只是相当于把C系的问号冒号换成 if-else 而已,仅是三元运算符的符号,而不是改了 if 条件判断的用途。
if
fun main()
{
val value = 10
val ret = if (value == 1)
{
"匹配 1"
true
}
else if (value == 2)
{
println("匹配 2")
true
}
else
{
"其它"
false
}
println(ret)
}
when
fun main()
{
val value = 11
val ret = when (value)
{
1 ->
{
"结果为 1"
}
5 ->
{
"结果为 5"
}
10 ->
{
"结果为 10"
}
else ->
{
"其它"
}
}
println(ret)
}
fun main()
{
val items = listOf("小红", "小明", "小张")
// for 遍历
for (item in items)
{
print("$item,")
}
println()
// for 遍历索引
for (idx in items.indices)
{
print("$idx ${
items[idx]},")
}
println()
// forEach 遍历
items.forEach {
item ->
print("$item,")
}
println()
// forEach 遍历索引
items.forEachIndexed {
idx, item ->
print("$idx $item,")
}
println()
val map = mapOf("value1" to 1, "value2" to 2, "value3" to 3)
for ((key, value) in map)
{
print("$key:$value, ")
}
println()
map.forEach {
(key, value) ->
print("$key:$value, ")
}
println()
}
while 和 do-while 这一点和 C 系是一样的,前者是在每次循环开始前判断是否满足条件,后者是循环结束一次后判断一次条件
fun main()
{
var x = 0
var y = 10
while (x > 0)
{
print(x--)
print(" ")
}
println()
do
{
print(y--)
print(" ")
}
while (y > 10)
println()
}
这点和 C 系差不多,return 结束当前所在函数,break 终止所在的一层循环,continue 所在循环的当此循环结束,进入所在循环的下一轮次循环。
不过 Kotlin 中有了标签的概念,标签名可以自己设置
break 只能跳出当前所在的一层循环,使用标签后可以跳到标签所在循环的外面,也就是可以一次性可以跳出指定层数的循环
fun main()
{
for (x in 1..3)
{
myLabel@ for (y in 1..3)
{
for (z in 1..3)
{
if (z > 1)
{
break@myLabel
}
println("$x $y $z")
}
}
}
}
continue 只能略过所在循环的当次循环,使用标签后,可以略过标签所在位置循环的当次循环,即可以略过指定层数的循环
fun main()
{
myLabel@ for (x in 1..3)
{
for (y in 1..3)
{
for (z in 1..3)
{
if (z > 1)
{
continue@myLabel
}
println("$x $y $z")
}
}
}
}
直接使用 return 会结束当前函数,在一些情况下只是需要局部返回,并不是要整个函数的返回。
fun foo()
{
val items = listOf(10, 11, 12, 13)
items.forEach {
item ->
if (item == 11)
{
return
}
println(item)
}
println("foo 结束")
}
fun main()
{
foo()
}
使用标签限制后,这里只会在当前 Lambda 表达式返回
fun foo()
{
val items = listOf(10, 11, 12, 13)
items.forEach myLabel@{
item ->
if (item == 11)
{
return@myLabel
}
println(item)
}
println("foo 结束")
}
fun main()
{
foo()
}
这里可以简化使用隐式标签,只要标签和接受这个 Lambda 表达式的函数同名就行
fun foo()
{
val items = listOf(10, 11, 12, 13)
items.forEach {
item ->
if (item == 11)
{
return@forEach
}
println(item)
}
println("foo 结束")
}
fun main()
{
foo()
}
Kotlin 比 Java 各方面都简洁很多,特别是入口函数。因为习惯了 C/C++,刚接触 Java 的时候太不理解把入口函数放在类中的行为,必须写个类来包起来就显得很复杂。我觉得还是像 C++ 形式的最好,支持函数式,同时支持面向对象,Kotlin 这点就类似。完全面向对象太怪异了,入口函数本身也没有作为类成员的意义,像是为了面向对象设计而强制这样搞一样,不是必要的,也可能是现阶段我没理解到它的精神吧。
另外一点 Java 要求入口函数必须有参数,即使不用也要写,这一点上 Kotlin 和 C/C++ 一样不用可以省略。
然而实际上这些工作只是 Kotlin 编译器帮我们做了
fun main()
{
println("你好,世界!")
}
将编译后的字节码文件反编译为 Java,可以看到编译器给加上了类,而用 Kotlin 写的入口函数只是作为成员函数被 Java 的入口函数调用,这样 Kotlin 中的入口函数就可以没有参数。
import kotlin.Metadata;
@Metadata(
mv = {
1, 9, 0},
k = 2,
d1 = {
"\u0000\b\n\u0000\n\u0002\u0010\u0002\n\u0000\u001a\u0006\u0010\u0000\u001a\u00020\u0001¨\u0006\u0002"},
d2 = {
"main", "", "untitled"}
)
public final class MainKt {
public static final void main() {
String var0 = "你好,世界!";
System.out.println(var0);
}
// $FF: synthetic method
public static void main(String[] var0) {
main();
}
}
但是如果给入口函数加上参数,那么此时 Kotlin 中入口函数和 Java 的入口函数就等同于一样了
fun main(args: Array<String>)
{
println(args.size)
}
import kotlin.Metadata;
import kotlin.jvm.internal.Intrinsics;
import org.jetbrains.annotations.NotNull;
@Metadata(
mv = {
1, 9, 0},
k = 2,
d1 = {
"\u0000\u0014\n\u0000\n\u0002\u0010\u0002\n\u0000\n\u0002\u0010\u0011\n\u0002\u0010\u000e\n\u0002\b\u0002\u001a\u0019\u0010\u0000\u001a\u00020\u00012\f\u0010\u0002\u001a\b\u0012\u0004\u0012\u00020\u00040\u0003¢\u0006\u0002\u0010\u0005¨\u0006\u0006"},
d2 = {
"main", "", "args", "", "", "([Ljava/lang/String;)V", "untitled"}
)
public final class MainKt {
public static final void main(@NotNull String[] args) {
Intrinsics.checkNotNullParameter(args, "args");
int var1 = args.length;
System.out.println(var1);
}
}
一般函数定义格式
// 默认返回值类型为 Unit
fun foo1()
{
}
// 显式返回值类型
fun foo2(): Unit
{
}
// 参数格式
fun sum1(num1: Int, num2: Int): Int
{
return num1 + num2
}
// 单行语句简化
fun sum2(num1: Int, num2: Int): Int = num1 + num2
// 参数默认值
fun sum3(num1: Int, num2: Int = 10) = num1 + num2
fun main()
{
println(sum1(9, 1))
println(sum2(11, 9))
println(sum3(10))
println(sum3(10, 20))
}
infix fun Int.sum(num: Int) = this + num
infix fun Float.sum(num: Int) = this + num
fun main()
{
println(4.sum(6))
println(13 sum 7)
println(6.7F sum 4)
}
函数名相同,参数个数或类型不同
fun foo()
{
println("空参数")
}
fun foo(str: String)
{
println(str)
}
fun foo(num: Int)
{
println(num)
}
fun main()
{
foo()
foo("你好,世界!")
foo(10)
}
fun main()
{
// 无参数
val printHello: () -> Unit = {
println("你好,世界!") }
printHello()
// 带参数
val showInfo: (String, Int) -> Unit = {
name, age -> println("$name $age") }
showInfo("小红", 20)
// 带类型推断
// 无显式声明参数类型,it 关键字代表参数
val greet: (String) -> Unit = {
println("你好,$it") }
greet("小强")
// 作为函数参数
// 为了提高代码可读性,一般使用不同关键词区分用途
// action:用于执行某种副作用操作或处理
// operator:用于定义运算符重载或特定的逻辑
// callback:作为回调函数的参数
// predicate:作为谓词函数的参数
// transform:作为转换函数的参数
// comparator:作为比较器的参数
// validator:作为验证函数的参数
fun funGreet(name: String, action: (String) -> Unit)
{
action(name)
}
funGreet("小明", greet)
fun calculate(x: Int, y: Int, operator: (Int, Int) -> Int): Int
{
return operator(x, y)
}
// 简化形式
val sum = {
x: Int, y: Int -> x + y }
println(sum(4, 6))
println(calculate(11, 9, sum))
}
fun show(vararg items: String)
{
items.forEach {
print("$it ")
}
println("\n-------------------------")
}
fun show(s: String, vararg items: String)
{
println(s)
items.forEach {
print("$it ")
}
println("\n-------------------------")
}
fun show(s1: String, s2: String = "AAA", vararg items: String)
{
println(s1)
println(s2)
items.forEach {
print("$it ")
}
println("\n-------------------------")
}
fun main()
{
val items = arrayOf("苹果", "香蕉", "菠萝")
show(*items)
show(s = "水蜜桃", *items)
show("芒果", "柿子", items = items) // 指定参数的时候,可以不加星号(展开数组,将每个元素作为一个独立参数传递),将整个数组作为一个参数传递
show(s1 = "榴莲", items = items)
}
class ClassA
{
var num1 = 0
// get 方法在取属性值时会调用
// set 方法在给属性赋值时会调用
// 下面的 get 和 set 是会默认添加的,可以省略
// field 关键字代表变量自身
var num2 = 0
get() = field
set(value)
{
field = value
}
// 可以根据需要修改取值赋值规则
var num3 = 0
get() = field + 2 // 取值结果加 2
set(value)
{
field = value - 3 // 赋值结果减 3
}
}
fun main()
{
// 创建对象
val ca = ClassA()
ca.num1 = 10
println(ca.num1)
// num3 定义的取值赋值方法是默认会添加的
// 所以最终效果和 num1 不重写取值赋值方法的一样
ca.num2 = 10
println(ca.num2)
// 演示自定义的取值赋值方法
println(ca.num3) // num3 初始值为 0,但是取值方法会 +2,所以会输出 2
ca.num3 = 10 // 赋值 10,但是赋值方法会 -3,所以实际赋值是 7
println(ca.num3) // num3 实际值是 7,但是取值方法会 +2,所以会输出 9
}
class ClassA
{
// 初始化代码段
init
{
println("A 执行初始化")
}
}
class ClassB constructor(name:String, age: Int) // 主构造函数
{
init
{
print("B 执行初始化:")
println("$name $age") // 主构造函数的参数可以在初始化代码段里使用
}
// 二级构造函数
// 需要使用主构造函数的参数,可以在此基础上添加额外参数
// 调用二级构造函数,首先会执行 init 代码块,再执行二级构造函数的代码块
constructor(name: String, age: Int, height: Float) : this(name, age)
{
print("B 二级构造函数:")
println("$name $age $height")
}
}
// 主构造函数可以省略 constructor 关键字
// 如果需要使用无参构造,则主构造函数不写参数,再写一个无参的二级构造函数即可
//
// 我觉得这种方案更接近于 C++/Python/Java 的构造函数风格
// 直接不管主构造函数,统一使用二级构造函数,显得较为简洁
// 至于 init 的存在,可以方便统一初始化,如果有多个构造函数
// 且都需要执行同样初始化可以放在里面,没需要可以不写 init
class ClassC
{
init
{
println("C 执行初始化")
}
// 用二级构造函数实现无参构造的效果
constructor()
{
println("C 无参构造函数")
}
// 用二级构造函数实现有参构造函数
constructor(name: String, age: Int)
{
print("C 有参构造函数:")
println("$name $age")
}
}
fun main()
{
val ca = ClassA()
val cb1 = ClassB("小明", 20) // 主构造函数
val cb2 = ClassB("小张", 19, 1.77F) // 二级构造函数
val cc1 = ClassC() // 无参构造函数
val cc2 = ClassC("小强", 22)
}
C++ 中只有 public、private 和 protected,Java 中没有 internal,但是有一个 default,default 在同一个包里可见,也是不使用修饰符时默认的修饰符,而 Kotlin 默认是 public,只不过感觉 Java 的 default 和 Kotlin 的 internal 实际意义相近。
abstract // 抽象类
final // 类不可继承,默认属性
enum // 枚举类
open // 类可继承
annotation // 注解类
文章浏览阅读1.5w次,点赞9次,收藏26次。简介一个创建某个文件到桌面快捷方式的BAT批处理.代码@echooff::设置程序或文件的完整路径(必选)setProgram=D:\Program Files (x86)\格式工厂.4.2.0\FormatFactory.exe::设置快捷方式名称(必选)setLnkName=格式工厂v4.2.0::设置程序的工作路径,一般为程序主目录,此项若留空,脚本将..._批处理创建桌面快捷方式
文章浏览阅读2k次。射频识别技术漫谈(6-10),概述RFID的通讯协议;射频ID卡的原理与实现,数据的传输与解码;介绍动物标签属性与数据传输;RFID识别号的变化等_芯片 ttf模式
文章浏览阅读1.1k次。今天小编心血来潮,为大家带来一个很有趣的项目,那就是使用 Python web 框架 Django 来实现支付宝支付,废话不多说,一起来看看如何实现吧。_django 对接支付宝接口流程
文章浏览阅读842次。Zabbix 5.0 LTS,跑了一年多了一直很稳定,前两天空间显示快满了,于是手贱清理了一下history_uint表(使用mysql truncate),结果折腾了一周。大概故障如下:然后zabbix论坛、各种群问了好久都没解决,最后自己一番折腾似乎搞定了。初步怀疑,应该是由于历史数据被清空后,zabbix需要去处理数据,但是数据量太大,跑不过来,所以来不及更新了(?)..._zabbix问题没有更新
文章浏览阅读296次。一、数据结构之字典 key-value
文章浏览阅读9.7k次,点赞3次,收藏13次。最近项目中遇到一个问题,是关于mybatis-plus的字段注解策略,记录一下。1问题调用了A组件(基础组件),来更新自身组件的数据,发现自己组件有个字段总是被清空。2原因分析调用的A组件的字段,属于基础字段,自己业务组件,对这个基础字段做了扩展,增加了业务字段。但是在自己的组件中的实体注解上,有一个注解使用错误。mybatis-plus封装的updateById方法,如果..._mybatisplus strategy
文章浏览阅读3.8k次。如何构建倒排索引,我们将这个过程叫做“索引构建”。如果我们的文档很多,这样索引就一次性装不下内存,该如何构建。硬件的限制 我们知道ram读写是随机的操作,只要输入相应的地址单元就能瞬间将数据读出来或者写进去。但是磁盘不行,磁盘必须有一个寻道的过程,外加一个旋转时间。那么只有涉及到磁盘,我们就可以考虑怎么节省I/O操作时间。【注】操作系统往往以数据块为单位进行读写。因为读一_为某一文档及集构件词项索引时,可使用哪些索引构建方法
文章浏览阅读836次。英特尔技术与制造事业部副总裁卞成刚7日在财富论坛间隙接受中新社记者采访时表示,该公司看好中国市场前景,扎根中国并以此走向世界是目前最重要的战略之一。卞成刚说,目前该公司正面临战略转型,即从传统PC服务领域扩展至所有智能设施领域,特别是移动终端。而中国目前正引领全球手机市场,预计未来手机、平板电脑等方面的发明创新将大量在中国市场涌现,并推向全球。持相同态度的还有英特尔中国区执行董事戈峻。戈峻
文章浏览阅读627次。https://blog.csdn.net/zrs19800702/article/details/53101213http://blog.csdn.net/lzw06061139/article/details/51445311https://my.oschina.net/linuxhunter/blog/654080rgw 概述Ceph 通过radosgw提供RES..._radosgw -c
文章浏览阅读3.7k次,点赞6次,收藏9次。我为什么选择ECharts ? 本周学校课程设计,原本随机佛系选了一个51单片机来做音乐播放器,结果在粗略玩了CN-DBpedia两天后才回过神,课设还没有开始整。于是懒癌发作,碍于身上还有比赛的作品没交,本菜鸡对硬件也没啥天赋,所以就直接把题目切换成软件方面的题目。写python的同学选择了一个时间序列数据的可视化曲线程序设计题目,果真python在数据可视化这一点性能很优秀。..._echarts 时间序列
文章浏览阅读1.6k次。事件类:/** * * * @className: EarlyWarnPublishEvent * * @description:数据风险预警发布事件 * * @param: * * @return: * * @throws: * * @author: lizz * * @date: 2020/05/06 15:31 * */public cl..._applicationeventpublisheraware
文章浏览阅读1.2k次。如需转载请注明出处!点击小图片转到图片查看的页面在Android开发中很常用到,抱着学习和分享的心态,在这里写下自己自定义的一个ImageView,可以实现类似微信朋友圈中查看图片的功能和效果。主要功能需求:1.缩放限制:自由缩放,有最大和最小的缩放限制 2居中显示:.若图片没充满整个ImageView,则缩放过程将图片居中 3.双击缩放:根据当前缩放的状态,双击放大两倍或缩小到原来 4.单指_imageview图片边界回弹