网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
operator fun LocalDate.minus(days:Long):LocalDate{
val date = LocalDate.now().minusDays(days)
return date
}
val twoDaysAgo = LocalDate.now()-2
println(twoDaysAgo.format(DateTimeFormatter.ISO_DATE))
…
2023-01-25
一元运算符的约定
讲完二元的,我们讲讲一元的,啥是一元运算符呢?比如i++自增,i–自减都是一元运算符,kotlin里面对这几个函数名做了约定,用operator修饰后可以用一元运算符去代替函数名
运算符 | 函数名 |
---|---|
+i | unaryPlus |
-i | unaryMinus |
!i | not |
++i,i++ | inc |
–i,i– | dec |
那我们尝试一下给LocalDate再增加一个自增运算符,这样一来,遇到获取明天,昨天日期的场景,我们连+1,-1都不用去写了,首先增加一个inc的扩展函数
operator fun LocalDate.inc(): LocalDate = LocalDate.now().plusDays(1)
然后我们获取一天后的日期可以这样去写
var today = LocalDate.now()
println(“今天的日期是
t
o
d
a
y
.
f
o
r
m
a
t
(
D
a
t
e
T
i
m
e
F
o
r
m
a
t
t
e
r
.
I
S
O
_
D
A
T
E
)
"
)
p
r
i
n
t
l
n
(
"
明天的日期是
{today.format(DateTimeFormatter.ISO\_DATE)}") println("明天的日期是
today.format(DateTimeFormatter.ISO_DATE)")println("明天的日期是{(++today).format(DateTimeFormatter.ISO_DATE)}”)
…
今天的日期是2023-01-28
明天的日期是2023-01-29
复合赋值运算符的约定
对于像+=,-=这样的有运算跟赋值两步操作的运算符我们称为复合赋值运算符,对应的约定函数是plusAssign和minusAssign,这个运算符除了可以像上面二元,一元那样操作之外,还可以针对一个集合做添加删除元素操作,比如
val dataList = mutableListOf(“kotlin”,“java”)
dataList += “c++”
dataList.forEach {
println(it)
}
…
kotlin
java
c++
其实我们就是给MutableList增加了一个plusAssign的函数,并且用operator去修饰它
operator fun MutableList.plusAssign(element:String){
add(element)
}
比较运算符的约定
equals
我们一般在查看一些用户行为日志的时候,比较一些日志是否为同一个用户的行为,我们会把用户的信息拿出来,再去比较用户的id是否相等,代码实现起来就像这样
data class People(var nickName:String,var userId:Int)
val userA = People(“Coffee”,12345)
val userB = People(“Coffeeee”,12345)
val userC = People(“Coffee”,54321)
println(“A跟B
i
f
(
u
s
e
r
A
.
u
s
e
r
I
d
=
=
u
s
e
r
B
.
u
s
e
r
I
d
)
"
是
"
e
l
s
e
"
不是
"
同一个人
"
)
p
r
i
n
t
l
n
(
"
A
跟
C
{if(userA.userId == userB.userId)"是" else "不是"}同一个人") println("A跟C
if(userA.userId==userB.userId)"是"else"不是"同一个人")println("A跟C{if(userA.userId == userC.userId)“是” else “不是”}同一个人”)
…
A跟B是同一个人
A跟C不是同一个人
但是在判断用户是否为同一个人的时候,我们一般只会以用户id为准,不会去用其他字段,那是否可以把.userId省略?等号两边只去判断People这个对象呢?我们只需要将People里面的equals函数重写一下,然后将它约定成我们需要用的比较运算符就可以了
data class People(var nickName: String, var userId: Int) {
override operator fun equals(other: Any?): Boolean {
if (other !is People) {
return false
}
return this.userId == other.userId
}
}
val userA = People(“Coffee”,12345)
val userB = People(“Coffeeee”,12345)
val userC = People(“Coffee”,54321)
println(“A跟B
i
f
(
u
s
e
r
A
=
=
u
s
e
r
B
)
"
是
"
e
l
s
e
"
不是
"
同一个人
"
)
p
r
i
n
t
l
n
(
"
A
跟
C
{if(userA == userB)"是" else "不是"}同一个人") println("A跟C
if(userA==userB)"是"else"不是"同一个人")println("A跟C{if(userA == userC)“是” else “不是”}同一个人”)
…
A跟B是同一个人
A跟C不是同一个人
compareTo
现在我们来看另一个比较运算符compareTo,这次我们给People类增加一个age的属性,使用比较运算符来对比两人的年龄大小
data class People(
var nickName: String,
var userId: Int,
var age:Int=0
)
val userA = People(“Coffee”,123,23)
val userB = People(“Tea”,125,24)
println("Coffee的岁数 比 Tea ${if(userA > userB) “大” else “小”} ")
如果直接给两个People对象用>或者<运算符,编译器是会报错的,我们给People增加个扩展函数compareTo,让它里面对age做对比
operator fun People.compareTo(other:People) = (this.age - other.age)
这个时候编译器就不报错了,运行一下得到的结果为
Coffee的岁数 比 Tea 小
注意:如果定义的扩展函数,在标准库里面已经存在同样签名的函数,那么运算符的逻辑只会以标准库的为准,自己定义的函数里面的逻辑将会无效,因为成员函数的优先级比扩展函数要高
我们以String为例,在kotlin标准库里面,String也有同样签名的compareTo函数,说明就算我们不去给String约定一个compareTo的函数,它也是可以使用>或者<这样的运算符,逻辑是逐个比较两个字符串各个字符的ASCII码值的大小,比如下面这段代码
println(“123 > 32 ${“123” > “32”}”)
如果不将对比的两个字符串转成整数类型,那么对比出的结果一定是false,因为它们对比的是两个字符串第一位1与3的大小,如果说我们给String增加一个compareTo(String)的扩展函数,让它可以实现将字符串转成整数类型在对比,会有效吗?我们试试看
operator fun String.compareTo(other:String):Int{
return this.toInt() - other.toInt()
}
依然还是false,结果就不展示出来了,有兴趣的小伙伴可以自己跑下试试,这个就说明了如果标准库里面有同样签名的函数,自己约定的函数将不起作用,逻辑以优先级高的为准,而且我们也不用每次定义函数时候都去标准库里面找找到底有没有相同签名的函数,因为如果有,编译器会提示你这个函数在标准库里面已经存在相同签名的了,比如上面这个compareTo,其实它是有个警告的
说明这个扩展函数被成员函数给隐藏了
get与set的约定
kotlin里面对集合进行赋值或者获取一个值的时候,往往是通过list[index]这种方式来操作的,其实这个也就是集合类里面对get与set函数做了约定
而我们可以利用这种约定,用在其他场景上,比如一个接口的数据类有若干个字断,分别运用在业务场景的各个角落,而如果有一天服务端同学告诉你要更改某一个字断,我们是不是要连着去改所有业务场景中用过这个字断的地方,有的字断用的少还好,有的字断用的地方多可能一改就要改半天,这个时候我们可以尝试着在数据类里面约定个get函数(其实这种场景应该是用Gson里面的@SerializedName,这里就是对约定举个例子~),传进去的下标值就是访问属性的位置,这样应用层只需要访问下标值就可以了,不用去关心具体字断是什么,我们现在给People类增加一个get函数
data class People(var nickName: String, var userId: Int,var age:Int=0) {
operator fun get(index:Int):Any?{
if(index == 0){
return nickName
}else if(index == 1){
return userId
}else{
return age
}
}
operator fun set(index:Int,value:Any?){
if(index == 0){
nickName = value as String
}else if(index == 1){
userId = value as Int
}else{
age = value as Int
}
}
}
我们现在通过下标去访问一个People类的属性,并通过下标去改变一个People的属性
val people = People(“Tony”,12,30)
println(“name = ${people[0]},userid = ${people[1]},age = ${people[2]}”)
…
name = Tony,userid = 12,age = 30
people[0] = “Peter”
people[1] = 20
people[2] = 23
println(“name = ${people[0]},userid = ${people[1]},age = ${people[2]}”)
…
name = Peter,userid = 20,age = 23
in与rangTo的约定
这两个约定我觉得放在一起说比较好,因为in表示判断是否在某一个区间里面,而rangeTo表示的是某一个区间,经常放在一起使用,比如我们在用到for循环的时候,遍历1到10这十个数字并打印出来,我们会这样做
for(i in 1…10){
println(i)
}
…
1
2
3
4
5
6
7
8
9
10
除此之外,我们还可以用in和rangeTo来判断某个日期在不在一个时间段里面
val date = LocalDate.now()
val anotherDate = LocalDate.of(2022, 12, 12)
val startDate = LocalDate.of(2022, 12, 22)
val endDate = LocalDate.of(2023, 2, 22)
println(“$date
i
f
(
d
a
t
e
i
n
s
t
a
r
t
D
a
t
e
.
.
e
n
d
D
a
t
e
)
"
在
"
e
l
s
e
"
不在
"
范围里面
"
)
p
r
i
n
t
l
n
(
"
{if (date in startDate..endDate) "在" else "不在"}范围里面") println("
if(dateinstartDate..endDate)"在"else"不在"范围里面")println("anotherDate ${if (anotherDate in startDate…endDate) “在” else “不在”}范围里面”)
…
2023-01-29 在范围里面
2022-12-12 不在范围里面
解构声明
对于一些属性比较多的数据类,如果想要单独把这些属性拿出来放在一个变量里面,可能需要写好几行赋值语句,比如People类,现在要生成一个名字叫Tony,年龄30岁,用户ID是10的对象,然后再用三个变量保存People的三个属性,代码如下
val people = People(“Tony”,10,30)
val name = people.nickName
val userId = people.userId
val age = people.age
如果属性多一些,赋值语句就更多了,kotlin里面有更简洁的作法
val (x,y,z) = People(“Tony”,10,30)
println(“name = $x id = $y age = $z”)
…
name = Tony id = 10 age = 30
深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上鸿蒙开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
g-blog.csdnimg.cn/direct/743b668910224b259a5ffe804fa6d0db.png)
[外链图片转存中…(img-ObS0CHQA-1715312744294)]
[外链图片转存中…(img-L6TuHdci-1715312744294)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上鸿蒙开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新