kotlin02.基础语法

292 阅读4分钟

变量申明

  • 通过var关键字申明可变属性
  • 通过val关键字申明不可变属性
  • kotlin都是引用类型,无值类型
  • 基本类型包括Int,String,Double,Float,Long,Short,Char,Boolean,基本与java一致。
var hello:String = "hello"
val world:String ="world"
hello = "hello2" //ok
world = "world2" // 提示常量不可以修改

函数

函数申明包含五部分

  • 作用域 private/public,默认情况下所有函数都是public
  • 关键字 fun
  • 函数名
  • 参数列表
  • 返回值
private fun test(name:String,age:Int):String

默认参数

在申明函数时,可以指定默认参数

private fun test(x : String , y:Int = 0): String {
    return "$x $y - ok"
}

参数顺序

函数发生调用时, 可以通过形参名来指定传入的参数,而不用管参数顺序.如果不指定形参名,则需按照顺序传入参数.

fun main() {
    println( test(y=12 , x="123"))
}

private fun test(x : String , y:Int ): String {
    return "$x $y - ok"
}

函数返回值

  • 可以指定函数的返回值类型
  • 当没有指定返回值类型时,默认返回的是kotlin.Unit类型
private fun testA(){

}
println(testA()) // kotlin.Unit

TODO

  • TODO在kotlin中是一个函数,这个函数会抛出异常,返回Nothing类型
  • Nothing是一个class,无法初始化
private fun testA(x : String , y:Int ): String {
    TODO("to be done")
}
//输出
Exception in thread "main" kotlin.NotImplementedError: An operation is not implemented:  to be done
	at ClassTestKt.testA(ClassTest.kt:110)
	at ClassTestKt.main(ClassTest.kt:106)
	at ClassTestKt.main(ClassTest.kt)

特殊名字的函数

如包含空格,或者特殊字符的函数,用反引号引用,它的主要作用是当出现kotlin和java混编时,一些java中的函数命名在kotlin中可能是关键字,比如is,这时候引用需要使用``来标记

`a special func`()
private fun `a special func`(){
    println("special func....")
}

匿名函数

  • 匿名函数的类型由参数和返回值决定 ()->String
  • 匿名函数的最后一条语句就是函数的返回值,不需要显式填写return
  • 替代java中的匿名内部类或者接口的传递
  • 匿名函数又经常被成为lambda表达式

匿名函数定义

匿名函数定义 var 函数名:函数类型 = {函数体}

//完整申明
var classTest1:(String , Int)->String = {name,age->
    "classTest1"
}

println(test()) //test
println(test) //(String , Int) -> kotlin.String
println(test is Function<String>) //true

//简化申明
var test = {println("this is a function")}

匿名函数的参数

  • 匿名函数可以不带,或带多个参数
  • 当只有一个参数时,在匿名函数的函数体中用it表示这个参数
  • 匿名函数的参数类型放在函数类型定义中,参数名放在函数的函数体中
//两个参数必须指明参数名,如 s  ,i 
val test2 : (String , Int)->String = {s,i->
    "$s , $i ,1234"
}

//一个参数不需要指明,默认用it表示
val test3 : (String)->String = {
    "$it ,test3"
}

//无参类型,可以省略()->returnType,返回类型自动推断
val test  = {
    val s = "hello"
    "$s test3"
}

匿名函数作为函数参数

大括弧表示匿名函数

使用一对大括弧,表示一个匿名函数,根据形参类型来匹配函数参数。

println (test6({
    "1234"
}))

fun test6(x: ()->String): String {
    return "test6 ${x()}"
}

匿名函数是唯一参数或者最后一个参数的简写

最后面的括号可以省略

println (test6{
    "1234"
})

fun test6(x: ()->String): String {
    return "test6 ${x()}"
}

//lambda参数,从test7的括号内移出到括号外面了。
println(test7("jack "){name,i->
    "$name - $i - ok"
})

fun test7(name :String , x: (String , Int)->String): String {
    return "test6 ${x(name , 123)}"
}

省略写法与匿名函数的定义非常像,中间的=号为最重要的区分。

test7("jack "){name,i->
    "$name - $i - ok"
}

val test2  = {name:String,i:Int->
    "$name , $i ,test2"
}

函数内联 inline

  • inline将标记为内联的函数的函数体拷贝到调用的方法里
  • 主要目的是为了优化lambda表示式,匿名函数或者闭包
  • 当没有使用内联标记时,会有以下两种情况
    • 如果lambda没有捕获外部变量,也就是没有传入参数,lambda会自动产生一个静态类来表示lambda
    • 如果有捕获外部变量,则每次都会新创建一个对象

内联方式

fun main() {
    println(showABC{
        "name is $it"
    })
}

inline fun showABC(testC:(String)->String):String{
    var outer = "jack"
    return testC(outer)
}



public final class TestKt {
   public static final void main() {
      int $i$f$showABC = false;
      String outer$iv = "jack";
      int var3 = false;
      String var4 = "name is " + outer$iv;
      boolean var5 = false;
      System.out.println(var4);
   }

   // $FF: synthetic method
   public static void main(String[] var0) {
      main();
   }

   @NotNull
   public static final String showABC(@NotNull Function1 testC) {
      int $i$f$showABC = 0;
      Intrinsics.checkNotNullParameter(testC, "testC");
      String outer = "jack";
      return (String)testC.invoke(outer);
   }
}

非内联方式

public final class TestKt {
   @NotNull
   private static final Function0 abc;

   public static final void main() {
      String var0 = showABC(abc);
      boolean var1 = false;
      System.out.println(var0);
   }

   // $FF: synthetic method
   public static void main(String[] var0) {
      main();
   }

   @NotNull
   public static final Function0 getAbc() {
      return abc;
   }

   @NotNull
   public static final String showABC(@NotNull Function0 a) {
      Intrinsics.checkNotNullParameter(a, "a");
      return (String)a.invoke();
   }

   static {
      abc = (Function0)null.INSTANCE;
   }
}

函数引用传递

使用::来获取函数引用

fun main() {
    println(showABC(::testRef))
}

fun showABC(testC:(String)->String):String{
    var outer = "jack"
    return testC(outer)
}

fun testRef(name:String ):String{
    println("name $name ")
    return "abc"
}

函数作为返回值的两种写法

val func = testReturnFunc("1")
println(func("10"))

//返回匿名函数
fun testReturnFunc(a:String):(String)->String{
    return {input->
        "1000 + $input + $a"
    }
}

//返回著名函数
fun testReturnFunc(a:String):(String)->String{
    return fun(input:String):String{
        return "1000 + $input + $a"
    }
}

闭包

  • 函数和对其周围状态一起构成闭包,闭包使得函数内部可以对外部作用域定义的变量进行访问

闭包的作用域

fun test2(): () -> Int {
    var str = 0
    return fun(): Int {
        return str++
    }
}

fun main(args: Array<String>) {
    val makeFun= test2()
    println(makeFun())
    println(makeFun())
    println(makeFun())
    println(makeFun())
}

//decode的java代码 ,仅仅new了一次test2的对象
public final class TestKt {
   @NotNull
   public static final Function0 test2() {
      final IntRef str = new IntRef();
      str.element = 0;
      return (Function0)(new Function0() {
         // $FF: synthetic method
         // $FF: bridge method
         public Object invoke() {
            return this.invoke();
         }

         public final int invoke() {
            IntRef var10000 = str;
            int var1;
            var10000.element = (var1 = var10000.element) + 1;
            return var1;
         }
      });
   }

   public static final void main(@NotNull String[] args) {
      Intrinsics.checkNotNullParameter(args, "args");
      Function0 makeFun = test2();
      int var2 = ((Number)makeFun.invoke()).intValue();
      boolean var3 = false;
      System.out.println(var2);
      var2 = ((Number)makeFun.invoke()).intValue();
      var3 = false;
      System.out.println(var2);
      var2 = ((Number)makeFun.invoke()).intValue();
      var3 = false;
      System.out.println(var2);
      var2 = ((Number)makeFun.invoke()).intValue();
      var3 = false;
      System.out.println(var2);
   }
}

条件判断

if /else if /else

fun main() {
    var s = "test"
    println("${if(s is String) "is string" else "not string" } ")
}

三元表达式

  • kotlin没有三元表达式 可以采用 两种方式替代
if( bool ) b else c
//或者
b.takeIf{bool} ?:c

when

  • kotlin没有switch/case,而是采用when来代替,when可以返回不同类型的值
  • when必须有个else来做fallback
  • 所有需要if /else if的情况都可以用when来代替
var s = "test"
var result = when(s){
    "test"->1
    "hello"->"abc"
    "world"->3
    else -> 4
}

string模板

类似于groovy和dart,可以通过${},将变量替换为相应的值,同时支持语句执行

println("value = $result class = ${result.javaClass} ${if(s is String) "is string" else "not string" } ")

空安全null

  • java中运行时出现的异常都继承自RuntimeException,这些异常是不强迫要求try...catch的,但是其他编译器异常,需要处理。
  • kotlin基于java中经常出现的运行时异常NullPointerException做了改进,强迫在编译期对空进行处理。

除非另有规定,否则变量不能为空。

fun testNull(){
    var abc = "aaa"
    abc = null //编译器提示异常 Null can not be a value of a non-null type String
}

可空变量必须显式申明

通过在定义时,加上?,表示变量可为空.

fun testNull(){
    var abc:String? = "aaa"
    abc = null
}

空安全函数调用

安全调用操作符 ?.

当变量为空时,不会调用后续方法

var abc:String? = "aaa"
abc?.chars()

Elvis 操作符 ?:

  • 变量非空时为左边的值,为空时,取右边的值.
  • 将空类型变量安全的转化为非空类型
fun testNull(){
   var i = null
    var j = i ?: 10
    println(j)
}

!! 操作符

将可空类型变量转化为非空类型变量,如果变量为空,则抛出KotlinNullPointerException

var i:String? = null
println(i!!.length)

//输出
Exception in thread "main" java.lang.NullPointerException
	at ClassTestKt.main(ClassTest.kt:111)
	at ClassTestKt.main(ClassTest.kt)