Kotlin 经常使用语法篇

习惯用法和规范

类布局

一般,一个类的内容按如下顺序排列:java

  • 属性声明与初始化块
  • 次构造函数
  • 方法声明
  • 伴生对象

不要按字母顺序或者可见性对方法声明排序,也不要将常规方法与扩展方法分开。而是要把相关的东西放在一块儿,这样从上到下阅读类的人就可以跟进所发生事情的逻辑。选择一个顺序(高级别优先,或者相反) 并坚持下去。编程

将嵌套类放在紧挨使用这些类的代码以后。若是打算在外部使用嵌套类,并且类中并无引用这些类,那么把它们放到末尾,在伴生对象以后。设计模式

常量名称
bash

标有 const 的属性,或者 val 属性的对象应该使用大写、下划线分隔的名称:app

const val MAX_COUNT = 8  //const属于编译期常量复制代码
val USER_NAME_FIELD = "UserName"复制代码

保存带有行为的对象或者可变数据的顶层/对象属性的名称应该使用常规驼峰名称:ide

val mutableCollection: MutableSet<String> = HashSet()复制代码

保存单例对象引用的属性的名称可使用与 object 声明相同的命名风格:函数

val PersonComparator: Comparator<Person> = ...
复制代码

Lambda 表达式参数

在简短、非嵌套的 lambda 表达式中建议使用 it 用法而不是显式声明参数。而在有参数的嵌套 lambda 表达式中,始终应该显式声明参数。布局

在 lambda 表达式中返回

避免在 lambda 表达式中使用多个返回到标签。请考虑从新组织这样的 lambda 表达式使其只有单一退出点。 若是这没法作到或者不够清晰,请考虑将 lambda 表达式转换为匿名函数。优化

不要在 lambda 表达式的最后一条语句中使用返回到标签。ui

data数据类

data class User(val name: String, val age: Int)复制代码

Kotlin编译器会自动从主构造函数中声明的全部属性并会为User类提供如下功能:

  • 全部属性的 getters (对于 var 定义的还有 setters)
  • equals()
  • hashCode()
  • toString()
  • copy()

为了确保数据类生成的代码的一致性,通常知足主构造函数须要至少有一个参数,主构造函数的全部参数须要标记为 valvar,并且数据类不能是抽象、开放、密封或者内部的

请注意,对于那些自动生成的函数,编译器只使用在主构造函数内部定义的属性。如需在生成的实现中排出一个属性,请将其声明在类体中:

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]形式复制代码

kv 能够改为任意名字。

“if”表达式

fun foo(param: Int) { 
val result = if (param == 1) {
    "one" 
  } else if (param == 2) { 
    "two"
  } else {
    "three"
  }
}复制代码

使用条件语句

优先使用 tryifwhen 的表达形式。例如:

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> 中年龄最大的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(3, 5)) // 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.maxBy{ persion: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" 返回的值是元素自己(接受者自己);

相关文章
相关标签/搜索