深入kotlin - 基础语法

Posted 颐和园

tags:

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

变量常量

变量和 swift 一样,用 var 声明:

var str:String = "hello"

kotlin 同样支持类型推断,可以根据变量初始化值推断变量类型。因此上句可以写成:

var str = "hello"

常量和 swift 不同,用 val 而不是 let 声明:

val a = 10

这种常量值 kotlin 中叫做“不可变量”,表示该常量不可改变(除了声明的时候可以赋值)。

此外还有一种常量,用 const val,二者的区别在于:

  1. const val 默认是 public final static ,val 默认是 private final static ,二者的访问级别不同;
  2. const val 值允许在 top-level 和 object 中声明,二者的使用范围不同。
  3. 访问 val 时是通过 getter 访问器访问,而 const val 不是,二者效率不同。

从 swift 的角度看,可以把 const val 默认是 public static let,而 val 默认是 private let。

注释

和 swift 相同,分为单行注释(//),多行注释(/* */),文档注释(/** */)。同时,Kotlin 支持注释嵌套:

/*
	/*
	*/
*/

函数

fun sum(a: Int, b: Int): Int return a+b

函数定义的关键字使用 fun 返回值类型直接放在参数列表之后,参数定义形式:参数名:类型。

可以对函数直接赋值

fun sum(a: Int, b: Int) = a+b

如果函数体仅仅是一个表达式,则可以用该表达式对函数进行赋值,表达式计算结果即函数的返回值,返回值类型自动推断。

Void vs Unit

Java 中方法无返回值可以使用 void 关键字,在 kotlin 中则使用 Unit 替代(实际上Unit 不是一个 object 类型),也可以直接省略不写。但是与 void 不同,Unit 可以被 return 语句返回:

fun test(name: String): Unit // Unit 可以省略
  ... ...
	return Unit // 此句可以省略

单表达式函数

函数体如果只包含一个表达式,则可以定义为:

fun add(a: Int, b:Int) = a+b

返回类型省略,通过表达式类型推断。

显式返回类型

对于有方法体的函数,必须显示指定返回类型(除非返回 Unit, Unit 可以省略):

fun add(a:Int, b: Int): Int  // 返回类型不可省略,Kotlin 不会推断有函数体的函数返回类型
	return a+b

参数默认值

如果要为函数参数提供默认值,在参数类型后使用 = 即可。

此外,子类重写的方法会使用与父类相同的默认参数值。如果要覆盖的方法有默认参数,方法签名中不能重写默认值,比如:

open class A 
	open fun method(a: Int, b: Int = 4) = a + b

class B: A() 
  override fun method(a: Int, b: Int) = a + b 

B 中,method 方法不能再覆盖默认参数 b。如果你显式指定 b:Int = 4 编译会报错。也就是说,子类无法重写父类方法中的默认参数值。

如果默认参数位于其他无默认值的参数前面,比如:

fun test(a: Int = 1, b: Int) = println(a-b)

那么要使用这个默认值,那么其他参数必须显式指定参数名:

test(b = 3)

这是为了避免语义模糊,因为如果使用 test(3),那么根据参数顺序传参,会将 3 赋值给 a 而非 b,导致 b 没有初值。

显式指定参数名(具名参数)

有点奇怪,传参时指定参数名使用 = 号而非 : 号:

test(b = 2) // swift 语法: test(b: 2)

如果在调用方法时,既有位置参数又有具名参数,则所有的位置参数都必须位于具名参数之前,比如这样是可以的:

foo(1, x = 2)

反过来是不允许的:

foo(x = 2, 1)

注意,当在 kotlin 中调用 java 方法时,不能使用具名参数,因为字节码中不一定总说保留方法参数名。

可变参数

可变参数使用关键字 vararg 声明:

fun test(vararg args: String) 
	println(args.javaClass) // 打印:class [Ljava.lang.String]
	args.forEach  println(it) 

... ...
test("a","b","c") 

实际上,可变参数是一个数组类型。但 kotlin 不允许直接向可变参数赋值数组类型,必须进行转换:

test( args = *arrayOf("a","b","c"))

这里,*叫做分散运算符,可以将数组打散成为可变参数可以接受的类型。但是可以只传递一个元素:

test(args = "a")

方法只能有一个可变参数,如果它不是最后一个参数,那么其他参数必须通过具名参数传递,如果其后的参数是函数类型,那么还可以使用尾随闭包形式。

fun <T> convert2List(vararg elements:T):List<T> 
	val result = ArrayList<T>()
	elements.forEach  result.add(it) 
	return result

... ...
println(conver2List("A","B","C")) // 打印 ABC
var elements = arrayOf("A","B","C")
println(convert2List("1","2","3", *elements))// 打印 123ABC

*elements 中的 * 是分散运算符,可以将数组转换成与可变参数兼容的类型。

中缀运算符

函数可以通过中缀运算符来调用,需要满足 2 个条件:

  1. 必须是成员函数或者扩展函数
  2. 只有一个参数

定义时使用 infix 关键字:

class TestClass(private var a: Int) 
	infix fun add(b: Int) = this.a + b // 1

... ...
val testClass = TestClass(2)
println(testClass.add(5)) // 2
println(testClass add 5) // 3
  1. add 函数是一个成员函数,通过 infix 关键字实现了一个中缀运算。
  2. 以标准的方法调用方式进行调用。
  3. 以中缀运算符的形式进行调用。

内联函数

内联函数在编译时会将函数代码复制到调用处,从而减少函数栈的创建和释放开销。缺点是字节码体积增大。

定义内联函数使用 inline 关键字,除此外和普通函数并无差别。

inline fun myCalculate(a:Int, b: Int) = a+b

lambda 表达式

lambda 表达式相当于匿名函数,格式如下:

  1. 用 包裹
  2. 参数列表位于 -> 箭头符号之前,参数类型可以省略(但变量声明中不可省略)
  3. 执行语句位于 -> 之后
  4. 如果某个参数无用,可以用占位符 _ 表示
  5. 最后一个表达式的值作为 lambda 表达式的返回值(也可以使用"全限定语法’'显式地 return - return@方法名 )
  6. 内置 it 关键字

当函数的最后一个参数是 lambda 表达式,调用时可以采用尾随闭包形式。

val sub =  a: Int, b: Int -> a-b  // 带参数
val action =  println("hello world") // 无参数
val add: (Int, Int) -> Int =  a, b -> a+b  // 表达式中参数类型可以省略,但 add 变量的声明中,参数类型不能省略

strings.filter it.contains("h").forEach println(it) // 内置 it 关键字
strings.filter 
  val myFilter = it.length > 3
  return@filter myFilter // 全限定语法 return

匿名函数

匿名函数使用 fun 关键字定义,形式如下:

fun(x: Int, y:Int) = x+y

strings.filter(fun(item):Boolean = item.length>3) // 匿名函数作为参数

Package

Kotlin 中的 Package 概念和 Java 稍有不同,它的名字和存储它的目录结构不必相同,它更多是一种“命名空间”的概念,主要用于解决命名冲突。

智能类型推断

在 java 中,短长度的类型可以直接赋值给长长度类型的变量,比如将 int 赋值给 long。在 kotlin 中不允许,需要进行显示类型转换。

var a: Int = 0
var b: Byte = 127

a = b.toInt()

但是,在代码中,如果明确已经可以推断出某个变量的确切类型,那么这个变量就可以直接当成这个类型使用,甚至不需要程序员进行显式类型转换,kotlin 会自动进行这种转换,例如:

fun uppercase(x: Any): String? 
    if (x is String )
        return x.toUpperCase()
    return null

注意 x 参数可能是任意类型(类型不确定),所以我们使用了根类 Any。kotlin 的类型判断使用 is 关键字(相对于 Java 的 instanceof)。此外,注意在 if 判断之后,x 已经被 kotlin 自动识别为 String 了,所以可以直接调用 String 的 toUpperCase 方法而不用进行类型转换(如果是 Java 则需要)。这也是 kotlin 的类型推断和自动类型转换为我们提供的便利。

以上是关于深入kotlin - 基础语法的主要内容,如果未能解决你的问题,请参考以下文章

深入kotlin- 基础语法

深入kotlin- 基础语法

深入kotlin - 基础语法

深入kotlin - 基础语法

kotlin基础语法

深入kotlin - 范型