Kotlin 常用语法篇

Posted Android开发中文站

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kotlin 常用语法篇相关的知识,希望对你有一定的参考价值。

习惯用法和规范

类布局
通常,一个类的内容按以下顺序排列:
  • 属性声明与初始化块
  • 次构造函数
  • 方法声明
  • 伴生对象
不要按字母顺序或者可见性对方法声明排序,也不要将常规方法与扩展方法分开。而是要把相关的东西放在一起,这样从上到下阅读类的人就能够跟进所发生事情的逻辑。选择一个顺序(高级别优先,或者相反) 并坚持下去。
将嵌套类放在紧挨使用这些类的代码之后。如果打算在外部使用嵌套类,而且类中并没有引用这些类,那么把它们放到末尾,在伴生对象之后。
常量名称
标有 const 的属性,或者 val 属性的对象应该使用大写、下划线分隔的名称:
  
    
    
  
const  val MAX_COUNT =  8   //const属于编译期常量
val USER_NAME_FIELD =  "UserName"
保存带有行为的对象或者可变数据的顶层/对象属性的名称应该使用常规驼峰名称:
  
    
    
  
val mutableCollection: MutableSet< String> = HashSet()
保存单例对象引用的属性的名称可以使用与 object 声明相同的命名风格:
  
    
    
  
val PersonComparator: Comparator <Person> = ...
Lambda 表达式参数
在简短、非嵌套的 lambda 表达式中建议使用 it 用法而不是显式声明参数。而在有参数的嵌套 lambda 表达式中,始终应该显式声明参数。
在 lambda 表达式中返回
避免在 lambda 表达式中使用多个返回到标签。请考虑重新组织这样的 lambda 表达式使其只有单一退出点。如果这无法做到或者不够清晰,请考虑将 lambda 表达式转换为匿名函数。
不要在 lambda 表达式的最后一条语句中使用返回到标签。
data数据类
  
    
    
  
data  class User( val name: String,  val age:  Int)
Kotlin编译器会自动从主构造函数中声明的所有属性并会为User类提供以下功能:
所有属性的 getters (对于 var 定义的还有 setters)
  • equals()
  • hashCode()
  • toString()
  • copy()
为了确保数据类生成的代码的一致性,一般满足主构造函数需要至少有一个参数,主构造函数的所有参数需要标记为 val 或 var,而且数据类不能是抽象、开放、密封或者内部的
请注意,对于那些自动生成的函数,编译器只使用在主构造函数内部定义的属性。如需在生成的实现中排出一个属性,请将其声明在类体中:
  
    
    
  
data  class Person( val name: String) { var age:  Int =  0}
在 toString()、 equals()、 hashCode() 以及 copy() 的实现中只会用到 name 属性,并且只有一个 component 函数 component1()。虽然两个 Person 对象可以有不同的年龄,但它们会视为相等。
复制
在很多情况下,我们需要复制一个对象改变它的一些属性,但其余部分保持不变。copy() 函数就是为此而生成。对于上文的 User 类:
  
    
    
  
fun copy(name: String = this.name, age: Int = this.age) = User(name, age)
我们可以写成:
  
    
    
  
val jack = User(name =  "Jack", age =  1)
val olderJack = jack.copy(age =  2)
过滤 list
  
    
    
  
val positives = list.filter {  x -> x >  0 }
或者可以更短:
  
    
    
  
val positives = list.filter {  it >  0 }
遍历 map/pair型list
  
    
    
  
val  map = hashMapOf( "name" to  "zhangsan", "age" to  "26", "address" to  "hangzhou")
    for ((k, v) in  map) {
        println( "$k -> $v")
        println( "$k -> ${map[k]}")
   }
map的访问支持  map[key]形式
k、v 可以改成任意名字。
“if”表达式
  
    
    
  
fun foo(param: Int) { 
val result =  if (param ==  1) {
     "one" 
  }  else  if (param ==  2) { 
     "two"
  }  else {
     "three"
  }
}
使用条件语句
优先使用 try、if 与 when 的表达形式。例如:
  
    
    
  
return  if (x) foo()  else bar() 
return  when(x) {
  0 ->  "zero"
else ->  "nonzero"
}
优先选用上述代码而不是:
  
    
    
  
if (x)
    return foo()
else
    return bar() 复制代码
when(x) {
       0 ->  return  "zero"
       else ->  return  "nonzero"
}
注:二元条件优先使用 if 而不是 when,如果有三个或多个选项时优先使用 when。
对一个对象实例调用多个方法 (with)
  
    
    
  
class Turtle {
     fun penDown()
     fun penUp()
     fun turn(degrees: Double)
     fun forward(pixels: Double)
}
val myTurtle = Turtle()
    with(myTurtle) {  // 画一个 100 像素的正方形
       penDown()
        for(i  in  1. .4) {
       forward( 100.0)
       turn( 90.0)
       }
      penUp()
}
对象声明
Kotlin中object关键字在多种情况下出现,包括下面讲到的“伴生对象”,“对象表达式”以及现在讲的“对象声明”都使用了object关键字,可见object关键字用法多么广发和强大;
object关键字出现他们都遵循同样的核心理念:这个关键字定义了一个类,并创建了该类的实例,也就是说用object关键字在定义该类的同时创建了该类的对象;
在开发中我们通常会使用到单例模式,java中单例通过static字段存储实例对象,并将构造私有化,通过暴露出一个静态方法用来唯一访问实例,在kotlin中可以直接通多object声明这样的一个类,通过这种“对象声明”方式将类的声明和类的唯一实例结合在一起。
与类声明一样,一个对象声明同样可以包括属性,方法,初始化语句块等声明,唯一不允许的是构造方法,与普通类实例不同,对象声明在定义的时候就已经被创建,不需要在其他地方调用构造方法;
  
    
    
  
object Persion{
fun add(var a : Int , var b:Int): Int{
return a+b
}
}
val sum=Persion.add( 3, 5//单例调用
伴生对象
伴生对象也叫类内部的对象,其声明可以用 companion 关键字标记:
  
    
    
  
class MyClass {
     companion  object Factory {
         fun create(): MyClass = MyClass()
    }
}
可以省略伴生对象的名称,在这种情况下将使用名称 Companion:
  
    
    
  
class MyClass {
      companion  object { }
}
val x = MyClass.Companion
注意:伴生对象的成员看起来像java中的静态成员,在运行时他们仍然是真实对象的实例成员,而且,例如还可以实现接口:
  
    
    
  
interface Factory<T{
     fun create(): T
}

class MyClass {
       companion  object : Factory<MyClass> {
           override  fun create(): MyClass = MyClass() 
      }
}

val f: Factory<MyClass> = MyClass
当然,在 JVM 平台,如果使用 @JvmStatic 注解,你可以将伴生对象的成员生成为真正的静态方法和字段。
对象表达式
object关键字不仅可以用来声明一个单例对象、伴生对象,也可以用来声明一个匿名对象,匿名对象替代java中的匿名内部类的用法。
  
    
    
  
button.setOnClickListener( object: View.OnClickListener(){  override  fun OnClick(v:View){}})
除了去掉了对象名字以外,语法与对象声明相同,对象表达式声明了一个类并创建了该类的实例,但并没有为这个类或实例分配一个名字,通常来讲,他们都不需要一个名字,因为你会将这个对象作为一个函数的参数,如果需要分配一个名字给这个对象,你可以将这个对象存储到一个变量中:
  
    
    
  
val listener= object: View.OnClickListener(){  override  fun OnClick(v:View){}}
与java的匿名对象不同的是,java匿名对象只能扩展一个类或者实现一个接口,kotlin的对象表达式可以实现多个接口或者不实现接口。
需要注意的是,Kotlin的对象表达式并不是单例类型的,每次执行到该位置时候,就会重新创建一个新的实例。
例外与java最大不同是kotlin的对象表达式中可以访问被创建函数中的局部变量,但是在java中访问被限制在final类型变量上,但是kotlin中解除了这个限制,这就代表kotlin中对象表达式可以访问并修改被创建函数的局部变量,
  
    
    
  
button.setOnClickListener( object: View.OnClickListener(){
var count= 0
override  fun OnClick(v:View){
count++
}
})
Lambda 表达式
作为函数参数的代码块, lambda表达式通常作为一个参数传入函数中,也可以单独存储到一个变量中;在java 8 中jdk 中也支持了lambda编程风格,这也是java 语言在不断演变和优化中最让人望眼欲穿的功能, 那么使用lambda到底能带来那些有优势呢?
java中最普通的点击监听器实现:
  
    
    
  
button.setOnClickListener( new View.OnClickListener(){
@ override
public void OnClick(View view)
{
// todo
}
})
通过匿名内部类去传入一个监听器实例,并实现监听器的click方法,当我们有多个view需要实现这种点击实现,那我们就得写多个这种实现,虽然写起来很简单,但是确实为我们增加了代码量,无论从语言角度还是设计模式角度看待这个问题,良好的编程风格主要原则之一就是避免代码在任何地方重复, 那么lambda表达式就很好解决这个问题, kotlin和java8 之后实现是:
  
    
    
  
button.setOnClickListener{   // todo  }
这段代码和java匿名内部类做了同样的事情,但是更加简单易读,lambda被当作只有一个方法的匿名对象的替代品使用;
lambda同样跟集合搭配使用是kotlin 的一大特色,如找到一个list 中年龄最大的persion,在java中普遍实现是你会引入两个中间变量,一个用来保存最大年龄,而另一个用来保存最大年龄的人,然后遍历这个列表,不断更新这两个变量:
  
    
    
  
public void findMaxAgePersion(List<Persion> persions){
int maxAge= 0
Persion oldPersion= null
forEach(Persion persion:persions)
{
if(persion.age>maxAge){
maxAge=persion.age
oldPersion=persion
}
}
println(oldPersion)
}
List persions= new ArrayList<Persion>()
persions. add( new Persion( "zhangli", 16))
persions. add( new Persion( "wangpeng", 22))
findMaxAgePersion(persion)
Kotlin中实现:
  
    
    
  
val persions=listOf(Persion( "zhangli", 16),Persion( "zhangpeng", 26))
val persion=persions.maxBy{it.age}
println(persion)
我们通常在java中对集合做的大多数事情可以通过使用lambda或成员引用的库函数来更好的表达,这样代码就少很多,也变得更容易理解;
lambda语法结构:
  
    
    
  
{ x:Int, y:Int -> x+y }
Kotlin的lambda表达式始终用花括号包围,花括号中通过 箭头(->)将实参和函数体分开,
  
    
    
  
val sum={x: Int, y: Int -> x+y }
println(sum( 35))  // lambda表达式存储到变量中,可以当做普通函数通过实参正常调用
Kotlin中对lambda有些语法约定:
  • 如果lambda表达式作为函数最后一个实参,可以将lambda表达式放到括号外面;
  • 当lambda作为函数唯一的一个实参时,可以将函数括号直接省略;
  • 当有多个实参时候,即可以选择把lambda留在括号内强调它是一个实参,也可以放到括号外边;
  • 当函数有多个lambda实参需要传入,不能超过一个lambda表达式放到外面;
我们拿上面的persions.maxBy{it.age}案例来说:
亦可以写成:
  
    
    
  
     persions .maxBy({ persion:Persion -> persion.age})
作为最后一个参数,可将lambda放到括号外边:
  
    
    
  
    persions .maxBy(){  persion:Persion -> persion.age }
作为唯一参数,可省略函数空括号:
  
    
    
  
    persions .maxBypersion:Persion -> persion.age }
省略lambda参数类型(上下文自动推断类型)
  
    
    
  
persions.maxBy{ persion-> persion.age }   
使用默认参数名称
  
    
    
  
persions.maxBy{ it.age }   //it 是自动默认生成的参数
集合的函数式API
在kotlin中有很多跟集合操作相关的扩展函数,这种扩展函数无疑为我们减轻了很多负担,这使得Kotlin相比java语法显得更加简单和高效的地方之一,kotlin中在合适地方使用这些标准库函数可以帮助我们更加高效开发,同时使得我们代码结构和逻辑显得更加简洁和清晰;
filter
filter函数遍历集合,并返回符合传入lambda表达式为ture条件的集合元素,首先明白我们操作的是集合结构,并且返回的也是集合,filter函数帮助我们过滤出lambda表达式中符合条件的元素,举例说明:
  
    
    
  
var  list=listOf( 1, 2, 3, 4)
prinltn ( list.filter() { it %  2== 0 })
// [2,4]  只有偶数留下来
上面结果返回的是一个新集合,集合中只包含那边符合lambda中判断式的元素,即:filter函数选出了匹配给定判定式的元素;
  
    
    
  
val persions= listOf(Persion( "zhangsan", 30),Persion( "wangwu", 32),Persion( "liuxing", 21))
println(persions.filter{
it.age< 30
})
// Persion("liuxing",21)
map
filter函数不会改变这些元素的值,帮助我们从这些集合中筛选出符合条件的元素,如果需要对集合元素做修改或者变换需要用到map操作符;
map函数对集合中每个元素应用给定的函数并把结果放到一个新集合中;
  
    
    
  
var  list=listOf( 1, 2, 3, 4)
prinltn ( list. map { it * it })
// [1,4,9,16]  
同filter函数,返回的是一个新的集合,原集合数据并没有修改或者破坏,并且新集合包含的元素个数不会变化,只是对集合中元素应用了给定的函数变换,即:map对集合中每个元素应用了lambda表达式;
  
    
    
  
val persions= listOf(Persion( "zhangsan", 30),Persion( "wangwu", 32),Persion( "liuxing", 21))
println(persions. map{
     it.name
})
// [zhangsan,wangwu,liuxing]
也可以使用成员引用写法:
  
    
    
  
println( persions .map( Persion ::name))
all
all函数检查集合中所有元素是否满足给定判定式,返回值是Boolean类型的,例如:
  
    
    
  
val peoples=listOf(Persion( "zhangsan", 27),Persion( "wangwu", 22))

println(peoples.all{it.age> 25})

// false
any
any函数检查集合中是否存在某个元素符合给定判断式,返回值也是Boolean类型,例如:
  
    
    
  
val peoples=listOf(Persion( "zhangsan", 27),Persion( "wangwu", 22))

println(peoples.any{it.age> 25})

// true
注意:!all 加上条件等价于用any加上这个条件的取反来替换,反之亦然!
count
如果你想知道有多少个元素符合条件,可以使用count函数将结果返回,例如:
  
    
    
  
println(peoples{

it.age> 20

})

// 2 
find
要找到第一个满足给定判定式的元素,可以使用find函数,例如:
  
    
    
  
println(peoples. find{
it.age> 25}
)
// [  "zhangshan"27 ]
with
"with" 函数用来对同一个对象执行多次操作,不需要反复引用对象名称;
"with" 返回的值是执行lambda代码的结果,该结果就是lambda中最后一行代码的值;
apply
"apply" 函数用来对同一个对象执行多次操作,也不需要反复引用对象名称;
"apply" 返回的值是元素本身(接受者本身);
作者:Scus
链接:https://juejin.im/post/5d4e747351882525d374e625

以上是关于Kotlin 常用语法篇的主要内容,如果未能解决你的问题,请参考以下文章

配置 VScode 编辑器 (前端篇)

如何从片段 KOTLIN 中调用意图 [重复]

如何在 Kotlin 片段内的按钮之间切换片段?

kotlin-从一个片段更改多个片段的小数位

如何更改片段 Kotlin

如何在kotlin的片段内显示网格视图?