Kotlin是一种静态语言,该语言可在Java虚拟机(JVM)上运行,并可被编译成JavaScript源程序,Kotlin语言在使用时可直接调用Java类库,并具有与Java程序进行互操作的能力。

Kotlin语言具有简洁、安全、支持跨语言互操作等技术特征,本文主要从基本语法、方法与Lambda表达式、类与对象、泛型等反面进行基本说明,偏重讲与Java语言不同的地方,建议有Java语言基础的同学学习。

本篇内容有所精简,阅读时如果感到有所跨越,可以阅读第二篇Kotlin语言入门学习(二)进行补充

 

一、基本语法

1.1基本数据类型

Kotlin语言支持的基本数据类型包含数字型、布尔型、字符和数组。

1.1.1数字:

Kotlin内置的数字类型有:Double、Float、Long、Int、Short、Byte

对于十六进制的数字,格式为0x数字,如:0xFF

对于二进制整数,格式为0b数字,如:0b001

Kotlin支持以下划线来分割数字,如:1_2345_6789

1.1.2类型转换

Kotlin程序中,位数短的数据类型不能直接转换成位数长的数据类型(与Java有区别)

不同类型的数字,可以使用toXXX()进行转换,如:转换为字节型10.toByte(),转换为字符123.toChar()

1.1.3数学运算

除基本的+,-,*,/,%(求模),Kotlin语言中的*运算符还可作为传值符号,支持将一个数组赋值给一个包含可变长输入参数的方法

位运算

shl(bits),类似于Java的<<运算

shr(bits),类似于Java的>>运算

ushl(bits),类似于Java的<<<运算

ushr(bits),类似于Java的>>>运算

and(bits),位上的和

or(bits),位上的或

xor(bits),位上的异或

inv(),位上取反

1.1.4字符

Kotlin语言中,字符使用类型声明符Char进行说明,字符数据必须使用单引号来表示

var c: Char = 'a'     表示将字符'a'赋值给变量c

1.1.5数组

Kotlin中创建数组使用arrayOf或arrayOfNulls方法

var s1: Array<String> = arrayOf("a","b","c")

var s2: Array<String?> = arrayOfNulls<String>(2)        //s2为一个长度为2的空字符串数组

Kotlin程序在声明变量时,如果变量类型后使用了符号"?",则表示该变量可为空,否则,表示不能为空

Kotlin中还定义了特定数组类,如ByteArray、ShortArray等

数组初始化可基于“工厂函数”实现

var ins = Array(5,{i->i+1})

Array语句第一个参数5表示数组长度,第二个参数{i->i+1}为一个工厂函数,表示将元素索引值加1,并将值设置为数组中对应元素的值,以上数组元素为1,2,3,4,5

1.1.6字符串

Kotlin中的字符串可使用模板表达式,基本格式为$标识名

val i = 9

val s = "$i is in the string"                //$i为一个模板表达式

val s1 = "\'$s\'.length is ${s.length}"           //$s和${s.length}为模板表达式

上述语句执行后,$i显示9,${s.length}显示18

1.1.7数据类型的检查和转换

数据类型检查使用操作符is或!is,如:检查变量a是否为一个整型时,使用if(a is Int){...},空值的比较用==

类型转换使用操作符as,当变量a为null时,var b:Int = a as Int为不安全转换,可使用var b:Int? = a as Int?进行控制。另外,可使用as?进行安全转换

 

1.2程序的控制结构

Kotlin程序中的控制结构包含if,when,for,while,其中,if和when可作为表达式直接使用

var value = if (a>b) {a} else {b}

when结构

               when(变量){

                        值1 ->语句1

                        值n ->语句n

                 }

类似于Java中的switch...case语句

1.3集合类型

除数组结构外,Kotlin中的集合类型包含列表List,集合Set,字典Map等;Kotlin中,集合类型分为可修改和只读两种

Kotlin中,只读列表基于List<T>接口定义,可修改列表基于MutableList<T>定义;类似,Set<T>为只读集合,MutableMap<K,V>为可修改字典等

初始化集合类型时,推荐直接调用系统提供的标准方法:listOf,mutableListOf,setOf,mutabeSetOf,mapOf,mutableMapOf等。复制一个集合类型的数据,可使用的方法为toMap,toList,toSet等

1.4数值范围

Kotlin可直接使用数值表达式:..(两个点),如:1..10表示范围1至10(整数)

在for循环中,使用for(i in 1..10)或for(i in 10 downto 1)

也可控制步长,for(i in 1..10 step 2),表示从1开始,每次前进2步,至10终止

不需要使用某个范围的终止值时,可使用until,如:for(i in 1 until 10)  表示数值范围从1开始,至9终止

1.5等式

Kotlin可使用两种等式运算符:===和==

==用于值或结构相等关系的判断,===用于应用对象相等关系的判断

1.6操作符

除常规操作符外,Kotlin还有在范围中进行查询或遍历的in操作符:in和!in

 

二、方法与Lambda表达式

2.1方法

                 fun 方法名称(参数列表): 返回值类型{

                         //执行语句

                         return 返回值

                   }

参数列表中参数声明的基本格式为“参数名:参数类型”。当方法没有返回值时,返回值类型可省略

 

当方法中的程序相对简单,仅包含计算表达式,并将计算结果返回时,可使用以下简化格式:

fun 方法名称(参数列表) = 计算表达式,如:fun add(a:Int, b:Int = 10) = a+b    //b指定了默认值

 

Kotlin中的一个方法只能包含一个变长参数,而且该变长参数只能位于方法定义中参数列表的末尾。变长参数需使用关键字vararg,如:vararg vs:Array<Int>表示vs是一个接收多个整型的参数

 

另外,当某一方法定义时满足了3个条件:定义时使用了infix关键字,方法只有一个输入参数,方法是类成员(或类的扩展方法),则该方法可以中缀方式使用,基本结构为:类实例 方法名 参数。

如:下述程序运行的结果会在输出窗口中显示“string-sub”:

                infix fun String.extends(str:String):String{

                       return this.toString() + str

                  }

 

                 fun main(args:Array<String>){

                                val s = "string"

                                val ss = s extends "-stub"

                                println(ss)

                  }

 

2.2方法的声明与使用

一个方法的使用是通过方法名来实现的。在使用一个方法时,还需要指定该方法的输入参数,如:add(2,3)为add函数的一种使用。

下列程序展示了本地方法的声明和使用(程序中,increase是本地方法,程序运行结果为6):

                        fun add(a:Int,b:Int):Int{

                              fun increase(c:Int):Int{

                              return c+1

                              }

                            return increase(a+b)

                          }

 

                         fun main(args:Arrat<String>){

                              println(add(2,3))

                          }

 

2.3Lambda表达式和高阶方法

Lambda表达式是一种匿名方法的表示方式。Lambda表达式一般使用箭头来表示一个运算操作,该操作分为3个部分:箭头,箭头左边,箭头右边。其中,箭头用于表示一个映射,箭头左边是映射的输入参数列表,箭头右边为映射的输出。

 

如:{x:Int,y:Int ->x+y},该运算有两个输入整型参数x和y,而运算结果(输出)为x+y。此外,Lambda表达式在声明时需要使用花括号,即{}

 

Lambda可被用于赋值给一个常量或变量,如:val add = { x:Float,y:Float -> x+y},这样,add实际上可被看作一个方法,该方法有两个输入参数x和y,输出x+y,使用时为add(0.1f,0.2f)。

 

方法声明也类似,箭头左边是输入参数类型列表,右侧为输出类型,如:(Int,Float) ->Float,该表达式表示方法的两个输入参数为Int和Float,输出参数类型为Float。方法不使用花括号

 

方法类型可看成是一种数据类型,并用于声明常量或变量。如:val calc:(Int,Float) ->Float = {x:Int,y:Float ->x*y},此例中,calc实质上是一个类型为(Int,Float) ->Float的运算,而具体的实现则被定义为{x:Int,y:Float ->x*y}。

在Kotlin中,所谓高阶方法是指方法中的参数是方法,或者方法的返回值是方法。如,在下列程序中,calc1和calc2为高阶方法:

                  fun main(args:Array<String>){

                               fun calc1(n:Int,f:(Int) ->Int):Int{

                                      return f(n)

                                }

                                println(calc1(10,{it+1}))

                                println(calc1(10,{i ->i-1}))

 

                               fun calc2(n:Int,fn:Float,f:(Int,Float) ->Float):Float{

                                             return f(n,fn)

                                }

                               println(calc2(10,0.2f,{i:Int,f1:Float ->i*f1}))

                               }

2.4匿名方法和闭包

除了Lambda表达式可用来定义匿名方法外,匿名方法还可直接被定义,如:

                        fun (x:Int,y:Float):Float = x*y

 

                        fun(x:Int,y:Float):Float{

                              return x*y

                         }

 

匿名方法和Lambda表达式可访问它们的闭包,也就是说可访问外部范围的变量。下列程序展示一种简单的闭包访问(结果为101):

                          fun main(args:Array<String>){

                                 var n = 100

                                 fun p(){

                                 n = n+1

                              }

                                 p()

                                 println(n)

                            }

上述程序中n可被看成一个main方法范围内的公共变量,而方法p与n在同一个方法内,所以该方法可以直接访问n,并进行计算。基于上述程序,一个闭包还可以这样使用:

              val print = println("testing")

 

              fun main(args:Array<String>){

                       print

               }

上述程序将print定义为一个语句,然后再main中直接调用,程序运行结束,输出窗口会输出“testing”。程序中print和main在同一个文件内,所以print可被视为文件内的公共变量,而main方法可直接使用print。

 

三、类与对象

3.1类的声明

与Java类似,使用class关键字

属性声明:var用于定义变量,val用于定义常量

如:var 属性名:属性类型 = ...

3.2类的构建器

Kotlin中的类有两种构建器:主构建器和非主构建器

主构建器使用constructor关键字说明

class 类名 constructor(参数列表){

var 属性名:属性类型 = ...

...

val 属性名:属性类型 = ...

}

若主构建器包含注释或访问权限说明,则关键字constructor不可省略,如:

class Machine public @Inject constructor(type:String){

...

}

否则,可省略关键字constructor

 

非主构建器

非主构建器的定义位于类定义的内部,使用关键字constructor说明:

class 类名{

constructor(参数列表){

         }

}

若一个类存在主构建器,在定义非主构建器时,该构建器要么需要调用构建器,要么需要调用另一个已定义的非主构建器。如:

class Machine(t:String,n:Int){

           val type = t

           val sum = n

//非主构建器1

           constructor(t:String):this(t,0)

//非主构建器2

           constructor(n:Int):this("equipment",n)

//非主构建器3

           constructor():this(0)

        }

上述程序包含1个主构建器和3个非主构建器,1和2通过this操作符调用主构建器来完成初始化工作,3调用非主构建器2完成初始化工作

若在程序创建时还无法确定属性的具体值时,相关属性需要使用lateinit进行说明,且lateinit所修饰的变量只能是可变更变量,如:

lateinit var txt:TextView

3.3设值器和取值器(setter和getter)

示例:

class SimpleClass(str:String){

        var att1 = str

        var att2:String? = null

        get(){       //自定义取值器

             if(field == null){          //field关键字指代一个属性实例,此处指att2

                return "an attribute"

             }else{

                return field

             }

          }

          set(s:String?){      //自定义设值器

               field = "att2 again"

                 }

          }

 

使用

fun main(args:Array<String>){

              var cls = SimpleClass("a class")

              println(cls.att1)

              cls.att1 = "a value"

              println(cls.att1)

 

              println(cls.att2)

              cls.arr2 = "att"

              println(cls.att2)

      }

 

程序运行结果如下:

a class

a value

an attribute

att2 again

 

3.4类的继承

Kotlin中允许被继承的类必须使用open关键字来进行说明,示例:

open class 父类名(参数列表){

}

继承结构:

...子类名(...):父类名(...)

示例:

open class SimpleClass(str:String){

var att1 = str

}

 

class MyClass(s:String):SimpleClass(s)          //子类MyClass没有程序内容,故省略程序体{...}

在继承中,父类没有使用主构建器,则子类可在声明时调用父类的非主构建器,子类也可以在自己的非主构建器定义时调用父类中的非主构建器(使用super),如:

open class SimpleClass{

          var att1:String

          constructor(s:String){

                 att1 = s

           }

          constructor(n:Int){

          att1 = n.toString()

                 }

          }

 

class MyClass(n:Int):SimpleClass(n)

 

class MyClass2:SimpleClass{

          constructor(s:String):super(s)

}

MyClass使用父类名调用父类的非主构建器,MyClass2使用super调用父类的非主构建器

 

方法覆盖:使用关键字override

父类中有方法open fun service():String{

        return att1

}

则子类覆写该方法时:

override fun service():String{

              return "service"

}

 

Kotlin中属性也可以覆盖,同样使用关键字open 和 override ,另外,var可覆盖val,但val不能覆盖var

 

3.5抽象类与接口

与Java类似,抽象类使用abstract关键字,接口使用interface关键字

同样,子类覆写方法时也要使用override关键字

Kotlin中允许使用抽象方法覆盖非抽象方法

如果抽象方法提供了默认实现,则子类可以不覆写该方法

3.6程序对象的可见性说明

Kotlin中可见性说明符有public、internal、protected、private,默认为public

其中,package包使用internal表示模块(构建工具指定的代码单元)内可见,protected为不可使用

类与接口使用internal时,模块内的程序可见,使用protected时,本类和子类可见

3.7扩展

Kotlin支持通过声明对类进行直接扩展,示例:

class MyClass(s:String){        //待扩展的一个类

         var att   = s

         fun show(){

               println(att)

         }

   }

          val MyClass.att1:String     //待扩展属性

          get() ="att1"

          fun MyClass.service(){    //扩展方法

          println("working with:"+att1)

          this.show()

          }

          fun main(args:Array<String>){

          val c = MyClass("cls")

          c.show()

          c.service()

           }

 

另外,也可以在不同类中进行扩展,如:在类B中对类A进行扩展

3.8数据类

数据类是一个持有数据的简单类,定义的格式为data class 类名(参数列表),编译器会为数据类增加以下内容:

equals方法,hasCode方法,toString方法,copy方法,componentN方法

数据类不能是abstract,open,sealed(密封类,限制继承),inner类型的类

3.9拆分结构

拆分结构的基本结构为:(变量或常量名,变量或常量名,...),下列程序中,一个Object对象中的数据项被分别设置到a,b,c变量中:

data class Object(var it1:String,var it2:Int,var it3:Float)

 

fun main(args:Array<String>){

      var obj = Object("item",1,0.1f)

      var (a,b,c) = obj

      println(a+":"+b+":"+c)

  }

 

四、泛型、对象表达式和代理

4.1泛型

泛型在使用时要加注符号<>,并在符号内设置泛型参数,一般用大写字母表示,如:<T>

Kotlin泛型在声明时可使用关键字out 和 in

当定义类的泛型声明中使用out,则带有指定类型的对象可赋值给带有该指定类型的父类型的变量或常量

当定义类的泛型声明中使用in,则带有指定类型的对象可赋值给带有该指定类型的子类型的变量或常量

4.2对象表达式

对象表达式用于声明一个匿名对象

基本使用场景:

方法名(object:接口名){

接口中方法的定义

}

或:

方法名(object:抽象类名()){

抽象类中方法的定义

}

对象表达式还可用于直接定义简单数据结构,如:

val 常量名 = object {

变量声明列表

}

示例:

val t = object{

val a = 100

}

4.3对象声明

基本语法:

object 对象名称{

属性声明列表

...

方法声明

}

对象声明可基于特定父类,语法结构为:

object 对象名称:父类名称(参数列表){

属性声明列表

...

方法声明

}

 

4.4类代理

类代理可以理解为:访问组件通过中间组件进行对服务组件的访问,使用关键字by

示例:

interface Inter{     //可访问接口

        fun service()

}

class SimpleClass1:Inter{     //服务组件1

          override fun service(){

          println("simple class one")

          }

}

class SimpleClass2:Inter{      //服务组件2

         override fun service(){

         println("simple class two")

         }

}

class Agent(i:Inter):Inter by i     //代理组件

         fun main(args:Array<String>){

                var c:Inter = SimpleClass1()

         Agent(c).service()    //通过代理组件访问服务组件1

         c = SimpleClass2()

         Agent(c).service()   //通过代理组件访问服务组件2

}

代理属性:语法为:var(或val) 变量名:变量类型 by 代理类名称()

Kotlin标准类库中还预定义了很多可以直接使用的代理工具,如:lazy和observable

Kotlin中的反省技术是针对程序或程序的运行实例进行分析和解释的技术,反省实现的基础是引用技术

如:查看类的注解,可使用反省实现

 

本文参考:基于Kotin的Android应用程序开发(薛岗)

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐