kotlin入门最容易教程一(最全,最详细)

0.环境准备

用到是IDEA免费版本。新建一个Kotlin项目。
在这里插入图片描述
取个名字。
在这里插入图片描述
最后新建一个.kt文件,就可以开始使用kotlin了。
在这里插入图片描述
选择创建File。之后就会生成一个.kt的文件。
在这里插入图片描述

HelloWorld程序:

fun main() {
    
    
    var str:String="Hello"
    println(str)
}

手动编译运行:
执行下面的代码会生成一个叫HelloKt.class的字节码文件,非常奇怪的是,命令行并没有提供什么参数来修改这个生成的文件名字。

kotlinc Hello.kt

通过kotlin命令行来执行字节码文件:

kotlin HelloKt.class

也可以通过下面的代码生成jar文件,然后通过java -jar来运行

kotlinc Hello.kt -d hello.jar
java -jar hello.jar

1.数据类型

1.1数据的声明

下面的代码声明了一个String类型的变量,通过var关键字来声明变量,和java相比,这个写法好像非常的麻烦,多了var关键字还多了冒号,而且写的顺序也不一样,类型声明在后面,据说这个语法是从TypeScript那里学过来的,对TypeScript(JS)不熟。既然设计成这样,一定是有他的好处的。实际上,类型可以省略,kotlin会自动帮我们推导出来。

fun main() {
    
    
    var str:String="Hello"
    println(str)
}

省略后的写法:
也就是不管什么类型,都可以声明为var类型。对于开发者来说,还是节省了一点时间的,虽然牺牲了一点效率,因为倒推类型肯定需要额外的内部操作。

fun main() {
    
    
    var str="Hello"
    println(str)
}

1.2只读变量

下面的代码声明了一个String类型的变量,但是编译器会给一个警告,说"Hello"是一个只读变量,可以用val声明。注意是最后一个字母是L而不是R,var和val是两个关键字。var可以用来声明一般的变量,val用来声明只读变量,类似于java的final关键字。

 var str="Hello"

用val声明:

 val str="Hello"

注意这个只读并不等于常量,常量是用另一个关键字const来修饰的。

1.3 const常量

常量只能声明在方法外部,在方法内部是不能声明常量的。

const val MAX=1024

fun main() {
    
    
    println(MAX)
}

在kotlin中,const相对于java的static final。我们可以通过双击shitf,输入kotlin bytecode查看字节码信息。

public final static I MAX = 1024

1.4 引用类型

我们知道Java有引用类型和基本数据类型这两种类型。引用类型放在堆里面,一般是java对象。而kotlin只有一种类型,也就是引用类型,但是在编译的时候,还是会转化为基本类型。

val age=10

在字节码里面的LOCALVARIABLE I代表int类型。也就是会转化为基本数据类型。字节码内容了解一下就行。

LOCALVARIABLE age I L1 L6 0

2. 表达式

2.1 if else表达式

和Java的用法一样,就不写了。

2.2 range表达式

字面意思就是范围表达式,用到的关键字是in。下面这个例子很好的说明了使用的in关键字的用法。不光可以指定数字,还可以指定数组或者集合。

    val score=66
    if (score in 0..60){
    
    
        println("bad")
    }else if (score in 61..80){
    
    
        println("not bad")
    }else{
    
    
        println("good")
    }

2.3 when表达式

相对于Java的switch表达式,但更加简洁,推荐将所有的if else表达式都转化成when的形式,when表达式在kotlin中是非常常用的。
下面的代码对比Java的switch语句少了case和default关键字看起来简洁了非常多。
而且可以返回多种数据类型,这里返回了String类型的“男”,“女”,和int类型的-1。
返回类似是Any类型,这个有点类似Java的Object类型。->实际上是一个lambda表达式。如果只有单行可以直接返回结果,不用写return;

    val gender=3;
    val result:Any=when(gender){
    
    
        1->"男"
        2->"女"
        else-> -1
    }
    println(result)

如果lambda表达式存在多行,那么需要加一个大括号{}。

    val gender=3;
    val result=when(gender){
    
    
        1->"男"
        2->"女"
        else-> {
    
    
            println("未知")
            println("程序退出")
        }
    }
    println(result)

程序最后输出如下,kotlin.Unit类似于Java的void,因为println并没有返回值。

未知
程序退出
kotlin.Unit

2.4 String模板

String模板就是String的拼接。在Java中,我们用++拼接字符串,这个实际上是非常麻烦的。很多语言都支持用 符 号 拼 接 内 容 , 用 符号拼接内容,用 {}拼接表达式。

用$拼接内容:

	val animal="dog"
    val color="yellow"
    println("The $animal's color is $color")

输出内容:

The dog's color is yellow

用${}拼接表达式:

    val animal="dog"
    val color="yellow"
    val age=10
    println("The $animal's is $color and ${if(age>5) "old" else "young"}")

输出内容:

The dog's is yellow and old

3.函数

3.1 函数头

kotlin的函数头和Java还是差别非常大的,priavate表示权限,默认是oublic,fun是函数声明关键字,括号里面是参数列表,最后的:Boolean表示返回类型。
可以看到参数列表和返回值的顺序和Java是相反的,这实际上是更加符合逻辑的,先传输入再确定输出类型,也就是输入输出。只是Java用习惯了,也就习以为常了。

private fun doSomething(i:Int ,s:String):String{
    
    
    return "resultCode";
}

函数调用
直接调用就可以,不需要创建对象。

fun main() {
    
    
    val flag = doSomething(5, "abc")
    println(flag)
}

3.2 参数的默认值

参数还可以给默认值,只是Java所没有的,Java有固定的默认值。kotlin支持指定默认值。

private fun doSomething2(a:Int=2,s:String="null"):String{
    
    
    return "$a $s";
}

fun main() {
    
    
   //没有传任何参数
   val result = doSomething2()
    println(result)
}

输出结果:

2 null

3.3 具名函数参数

具名的意思就是在传入参数的时候可以指定参数的名字,这样的目的是增加可读性,因为光传参数,不知道这个参数是干嘛用的,可能传错位置,可读性也很差。
例如下面的代码,代码一的位置,写了参数的名字是age,和name,这样就不会搞错参数位置了,可读性非常强。但是,IDEA是支持参数类型提示的,可以直接显示参数类型,这相对于是IDEA已经实现的功能,可能就是为了弥补Java没有这个功能而设计的。

fun main() {
    
    
    //代码一
    var flag=doSomething(age=5,name="abc")
}

private fun doSomething(age:Int,name:String):Boolean{
    
    
    return true;
}

3.4 Unit函数

在Java中没有返回类型就返回void类型,但在泛型的时候,这个概念就有点矛盾。kotlin用kotlin.Unit这个类型表示没有返回类型。在kotlin中,如果没有返回类型,那么函数的返回类型可以不用写任何类型,也不用写void,kotlin根本就没有void这个关键字。

fun main() {
    
    
    println(testUnit(5,"abc"))
}

private fun testUnit(age:Int,name:String) {
    
    
}

输出:

kotlin.Unit

3.5 反引号的函数名

在函数名上面加一个反引号,kotlin支持这种写法,这样做的主要目的是为了和Java的交互性。Java和Kotlin是可以无缝调用的,但因为Java和Kotlin的关键字不同,可能一些方法名在Java是合法的,但到了Kotlin就变成非法,这时候就可以加反引号解决。
例如下面的Java代码,声明了一个is方法,这在Java里面是合法的,但如果在kotlin里面要调用这个方法就不行了,因为is在Kotlin里面是一个关键字。

public class Data {
    
    
    public static void is(){
    
    

    }
}

这时候在函数名上加一个反引号就可以解决这个问题。这样is就不被当作是关键字而可以在kotlin里面使用了。

//在kotlin中调用
Data.`is`()

有趣的是,加了反引号之后,方法名甚至可以写空格例如下面的代码。

private fun `first we calculate add`(a: Int,b:Int):Int{
    
    
    return a+b;
}

private fun `second we calculate sub`(a: Int,b:Int):Int{
    
    
    return a-b;
}

fun main() {
    
    
    val add= `first we calculate add`(1,2)
    val sub = `second we calculate sub`(1, 2)
    println("$add $sub")
}

用自然语言编程不再是梦。当然这个应该是用不太上,主要还是为了解决和Java的交换问题,写类似注释的方法名还是交给注释比较好。

3.6匿名内部类

我们举String的count方法为例,count方法有两个重载,第一个无参函数非常好理解,第二个重载声明如下,要求我们传入一个lambda表达式,这个表达式传入char,返回boolean(判断条件),最后整个函数返回一个int,也就是我们指定的char字符有多少个。

public inline fun CharSequence.count(predicate: (Char) -> Boolean): Int
    var count="Kotlining".count()
    var countI = "Kotlining".count({
    
    letter -> letter == 'i'} )
    println(count)
    println(countI)

输出

9 
2

3.7 函数类型

这个和C语言的函数指针是非常像似的,但java是移除了这些特性的。
我们看下面的代码,第一次看这个代码可能非常的奇怪,看着像lambda表达式,又不完全是,其实这里的关键就是理解()->String的含义。这个语法是lambda表达式,但这里的真正含义是匿名内部类类型,并且这个匿名内部类返回String类型。顺理成章的,我们定义的变量result的类型也就变成了String类型。注意是一个类型声明,而不是lambda表达式实体,lambda表达式的实体是{}里面的内容。

	var result:()->String={
    
    
        val animal="dog"
        "This is $animal"
    }

看下和lambda表达式的对比:
下面是java的lambda表达式.一个非常明显的区别就是kotlin的匿名内部类没有return,而且在里面也是不可以写return的,这是因为他默认返回类型是表达式的最后一行。为了实现这个功能,就必须显示的声明类型,因为可能返回一个类对象。

	()->{
    
    
        val animal="dog"
        return "This is $animal"
	}

当然也是可以带参数的。在()里面声明参数类型,在{}开头可以写具体名字。写法还是非常飘逸的。

 val result2:(String,Int)->String={
    
    
        name,age->
        val animal="dog"
        "This is $animal , name is $name , age is $age"
    }

    println(result2("tom",5))

输出:

This is dog , name is tom , age is 5

还有一种类型推断的写法,如下,参数我们还是要指定类型的,但返回值可以推断出来。感觉这种写法更简洁一点,前面的写法更标准一些。

val result4 = {
    
     name: String, age: Int ->
        val animal = "dog"
        "This is $animal , name is $name , age is $age"
    }

3.8 it关键字

接着上小节,如果参数只有一个的时候,我们可以不写具体的参数名字,kotlin默认为我们提供了一个it关键字,可以直接使用这个关键字当参数变量名称。

  val result3:(String)->String={
    
    
        val animal="dog"
        "This is a $animal , name is $it."
    }

    println(result3("tom"))

输出:

This is a dog , name is tom.

3.9函数做为函数参数

下面这个函数的功能是显示学生信息,包括学生的姓名和身份证,第二个参数是一个函数类型,没错可以将一个函数做为参数传进来。这个函数可以合成学生的身份证,身份证由邮政编码,生日,编号构成。

private fun showStudentInfo(name: String, getId: (Int,Int,Int) -> String): String {
    
    
    //必须用大括号
    return "$name's id is ${
      
      getId(300027,20001212,1001)}"
}
	var getId = {
    
     code: Int, birth: Int, num: Int ->
            "$code$birth$num"
    }
    println(showStudentInfo("Tom", getId))

输出:

Tom's id is 300027200012121001

其实这个写法非常的奇怪。声明一个函数然后又去调用,那干嘛不直接写一个函数呢?其实,上面的写法是不合理的,一般不会这么写,问题就出在var getId这个变量上,我们写了这个变量来引用函数类型,这是没有必要的,因为我们用函数类型的目的就是为了写匿名函数,取名字完全是多此一举。可以简化写成下面的样子。

 println(showStudentInfo("Tom", {
    
     code, birth, num ->
        "$code$birth$num"
    }))

这样看起来就是非常简洁了。而且,kotlin有规定,如果只有一个函数类型,或者函数类型是最后一个,()可以省略。
函数类型是最后一个的情况:

  println(showStudentInfo("Tom") {
    
     code, birth, num ->
        "$code$birth$num"
    })

只有一个函数类型做参数的时候

   println(showStudentInfo {
    
     code, birth, num ->
        "$code$birth$num"
    })

这种写法一定要掌握,因为这个写法在kotlin里面是非常常见的。
这种写法一定要掌握,因为这个写法在kotlin里面是非常常见的。
这种写法一定要掌握,因为这个写法在kotlin里面是非常常见的。

3.10 内联函数

内联函数的主要用途就是为匿名函数提高性能。在调用下面的有函数类型做为参数的方法时,会使用到匿名内部类。第二个参数要求我们传入匿名内部类,对于编译器来说,匿名内部类的创建和普通类是一样的,但既然是匿名的,效率应该要更高写,如果也是和普通类一样创建,效率就比较低了。内联函数就是为了解决这个性能问题,那么是怎么解决的呢?答案是通过宏替换,如果你学过C语言,你就知道C语言的宏替换,内联函数也是一样的,通过宏替换,我们直接把匿名内部类的代码直接复制到调用的函数里面,这样就不需要创建类了,不需要创建类当然性能就提高了。

private fun showStudentInfo(name: String, getId: (Int,Int,Int) -> String): String {
    
    
    return xxx
}

使用方法就是直接添加一个inline关键字就可以了。

private inline fun showStudentInfo(name: String, getId: (Int,Int,Int) -> String): String {
    
    
    return xxx
}

3.11 函数引用

在调用含有函数类型的方法时,不光可以传入匿名内部类,也可以传通常的方法,这时候就需要用到函数引用。
例如下面的代码,foo方法的参数列表和showStudentInfo里面的getId函数类型的参数列表是一样的,这个时候可以把foo直接传给showStudentInfo。使用方法就是在::加普通函数名称。

private fun showStudentInfo( name:String, getId: (Int,Int,Int) -> String): String {
    
    
    //必须用大括号
    return   "$name's id is ${
      
      getId(300027,20001212,1001)}"
}

private fun foo(  a:Int,b:Int,c:Int ): String {
    
    
    return   "$a$b$c"
}

 println(showStudentInfo("Tom", ::foo))

输出:

id is 300027200012121001

3.12 函数类型做为返回类型

简单来说,就是一个函数返回另一个函数。什么?这有什么意义?如果有多个函数进行嵌套调用,那么就会形成函数之间的层层包含关系。这样的好处就是子函数可以共享父函数的变量,这种写法在脚本语言中是非常常见的,例如JavaScript。这样做的原因是脚本语言存在一个很大的问题,就是即使在不同的文件里面定义相同的变量名也会报错。因为脚本语言并没有像java这样有package和class的概念。kotlin也可以做为脚本语言。我们只能举个简单的例子来说明一下她的形式,理解的比较深还是需要大量的实践的。
看下面的代码,返回值是一个函数类型,传入一个String返回String。

private fun foo(a:Int):(String)->String{
    
    
    return {
    
    
        "$it"
    }
}

在main函数中执行,返回函数类型的变量bar,可以调用,传入"abc"返回"abc"

    var bar = foo(1)
    println(bar.invoke("abc"))

输出:

abc

猜你喜欢

转载自blog.csdn.net/ScottePerk/article/details/124819247