Kotlin基础——基础内容

1 函数和变量

1.1 基本程序

fun main(args: Array<String>) {
    println("Hello World!")
}

程序从“Hello World”打印开始,从这个简单的程序中,包含Kotlin语言的以下特性:

  • 函数使用关键字fun声明
  • 主函数依然为main函数
  • Kotlin中没有Java中数组类型,数组就是Array类
  • 函数可以定义在文件的最外层,可以不用放到类中
  • 参数类型在参数的后面
  • Java中的很多语句Kotlin做了更简易的封装,比如println
  • 语句结尾不需要分号

1.2 函数

fun max(a: Int, b: Int): Int {
    return if (a > b) a else b
}

函数仍然由函数名,参数列表和返回值类型组成。不同的是函数使用fun定义,参数类型和返回值类型在后面

  • Kotlin不支持三元运算符(?😃
  • Kotlin中很多逻辑运算是表达式,可以直接返回值

上面的可以简化为:

fun max(a: Int, b: Int): Int = if (a > b) a else b

这样的函数拥有表达式体,上面有大括号的函数拥有代码块体。
还可以进一步简化为:

fun max(a: Int, b: Int) = if (a > b) a else b

对于拥有表达式体的函数,可以省略其返回值类型,因为Kotlin具有类型推导能力。

1.3 变量

Java中声明变量从类型开始,而Kotlin中很多时候不需要声明变量的类型,因为Kotlin有强大的类型推导功能,能够根据上下文推导出具体的类型。

1.3.1 变量的类型推导

val a = 10
val b = 12.3
val c = "test"

a被推导为Int类型
b被推导为Double类型
c被推导为String类型
Kotlin中没有Java中的基本数据类型,所有类型都是类对象
如果要显示指定数据的类型,可以在变量后面加变量的类型

val d: Byte = 10

1.3.2 可变变量和不可变量

Kotlin中通常有两个变量修饰符

  • val(value):不能在初始化后再次赋值,对应于Java中使用final修饰的变量
  • var(variable):可变变量,对应于Java中的非final修饰符

默认情况下,应该首先使用val修饰符,当变量确实需要改变时,才替换成var修饰符

1.3.3 变量使用规则

在这里插入图片描述
val类型赋值之后不可改变,否则编译不过
在这里插入图片描述
如果变量不在定义的时候初始化,则需要确定其类型,因为Kotlin虽然可以类型推导,但还是静态类型的语言,每个变量都有确定的类型。不在定义的时候确定其类型,编译器无法推导,无法编译通过

val b: Int
if (a > 5) {
    b = 1
} else {
    b = 0
}

val变量只能赋值一次,如果编译器能够保证只执行一条初始化语句,则可以编译通过

val list = listOf(1, 3, 4)
list.addLast(5)

val变量定义的引用不可变,但是其引用对象的内容可变。list指向一个List对象,不能改变其引用直系指向,但是该List对象可以改变其中存储的元素
在这里插入图片描述
对于var可变类型变量,不能改变其数据类型,当a定义的时候,类型推导为Int类型,此时该变量的类型就已经被确定,不能改变其类型,否则编译失败

1.4 字符串模板

开发中很多时候涉及到字符串拼接,Java中拼接如下

"Hello, " + name + "!"

在Kotlin中可以使用更简便的写法,就是字符串模板

"Hello, $name!"

字符串模板不止可以引用简单变量,还可以引用复杂的变量

val names = listOf("World", "Kotlin")
println("Hello, ${names[0]}!")

这里可以使用大括号引用字符串数组的第一个元素

println("Hello, ${if (args.size > 0) args[0] else "World"}!")

大括号中还可以嵌套表达式

2 类和属性

Kotlin也是面向对象的语言,是JVM语言,在Java虚拟机上运行,所以也是有类和属性一说
Java中有一个JavaBean的概念,表达的是一种Java中规范类的写法

public class Person {
    private final String name;

    public Person(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

通常这些构造函数和get方法都是比较重复的代码,与具体的业务逻辑无关
可以将上面的代码拷贝到kotlin文件中,能够自动将Java代码转换成kotlin代码

class Person(val name: String)

Kotlin中只需要一行代码就能够表示上面的一段Java代码
public类修饰符消失了,因为在Kotlin中public是默认的可见性

2.1 属性

类的概念是把数据和处理数据的代码封装成一个单一的实体
类包含属性和行为,在Java中字段和访问器(settergetter)的组合常常被叫做属性
Kotlin中,属性是头等的语言特性,完全替代了字段和访问器方法
在类中声明一个属性和声明一个变量一样:使用val和var关键字,当声明属性的时候,就默认声明了对应的访问器。声明成val的属性是只读的,默认会提供对应的get方法,而声明成var的属性是可变的,默认会提供getset方法

class Person(val name: String, var isMarried: Boolean)

这里声明了两个属性,一个是只读属性,只提供getter方法,另一个是可变属性,提供gettersetter方法,使用Java访问Kotlin声明的类

public class Test {
    public static void main(String[] args) {
        Person person = new Person("zhangsan", false);
        person.setMarried(true);
        System.out.println(person.getName());
        System.out.println(person.isMarried());
    }
}

输出

zhangsan
true

Kotlin声明的属性,是一个类的字段都是私有的类,在Java代码中可以访问Kotlin声明的类,可以看到,声明为val的属性,只有getter方法,声明为var的属性,有gettersetter方法。
如果属性声明为XXX,默认提供的是setXXXgetXXX方法,其中有一个例外是isXXX开头的属性,这种属性,默认提供的是setXXXisXXX方法,需要注意。
上面是Java中访问Kotlin中声明的类,下面代码为Kotlin中访问

fun main(args: Array<String>) {
    val person = Person("zhangsan", false)
    person.isMarried = true //1
    println(person.name) //2
    println(person.isMarried)
}

上面的Kotlin代码中,看起来像是直接访问的定义的属性。但是上面说过,Kotlin中声明的属性变量,默认是private的,Kotlin中也没有改变类中private变量无法从类外访问的特性。Kotlin中,不需要显示调用gettersetter方法,而是像访问public属性一样调用访问器。注释1处默认调用的是person对象的isMarried类变量的setter方法,而注释2处则是调用的getter方法。

2.2 自定义访问器

上面属性默认的访问器都是编译器生成的,而如果要实现自己的逻辑,就需要自己自定义访问器

class Rectangle(val height: Int, val width: Int) {
    val isSquare: Boolean
        get() {
            return height == width
        }
}

Rectangle类定义了一个属性isSquare,该属性用val修饰,只提供getter方法,而这个getter方法没有使用默认的getter,而是使用自定义的getter方法。自定义访问器就是对属性创建一个get函数或者set函数来自定义getter方法和setter方法。

2.3 目录和包

kotlin文件中以package语句开头,文件中定义的所有声明(类、函数、属性)都在该包中,如果其他文件中定义了相同的包,则他们属于同一包中,可以直接使用,如果包不同,则需要导入,导入使用import关键字。

2.3.1 同包访问

TestOne

package com.test

class TestOne(val num: Number)

fun TestOnePrint() {
    println("TestOnePrint")
}

TestTwo

package com.test

class TestTwo(){
}

fun main() {
    val testOne = TestOne(3)
    TestOnePrint()
}

TestTwo文件中的main函数可以直接访问TestOne文件中。
与Java不同的是Java中包路径与项目的实际路径对应,一个点是一个文件夹,而在Kotlin中,包名可以不与文件路径相对应,可以将该文件放到项目中的任意位置,但是他们仍处于相同的包下,且不会导致编译错误。

2.3.2 不同包导入

Kotlin中import可以导入任何声明
导入顶层函数

import com.test.TestOnePrint

导入类

import com.test.TestOne

导入包中所有元素

import com.test.*

2.3.3 包名类名定义规则

类名:
首先来看Java,Java中一个类文件可以包含多个类定义,但是只能有一个public定义的,且必须和类文件相同。
而对于Kotlin来说,则没有这么严格的显示,同样的,一个文件中可以定义多个类,类文件文件名可以任意选择。
包名:
对于包名来说,虽然Kotlin没有限制,但是建议参考Java中的实现方式,能够使代码结构更加清晰

3 枚举和“when”

3.1 声明枚举类

enum class Color {
    RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET
}

kotlin中创建枚举类比Java多一个关键字,需要enum class一起使用,enum是一个软关键字,只有放在class前面才有意义。

3.2 when结构使用

fun printColor(color: Color) {
    when (color) {
        Color.RED -> println("red")
        Color.BLUE -> println("blue")
        Color.VIOLET -> println("violet")
        else -> println("no such color")
    }
}

kotlin中没有switch...case,kotlin中多重判断使用when结构。

fun getWarmth(color: Color) = when (color) {
    Color.RED, Color.ORANGE, Color.YELLOW -> "warm"
    Color.GREEN -> "neutral"
    Color.BLUE, Color.INDIGO, Color.VIOLET -> "cold"
}

when结构也可以合并处理
需要注意的是when结构使用枚举类的时候,需要对所有变量进行定义,或者定义else,否则无法编译通过

3.3 when结构包含对象

when结构比switch更加强大,switch只能使用常量(枚举常量、字符串常量或者数字字面量),而when结构可以使用任何类型对象

fun max(c1: Color, c2: Color) = when (setOf(c1, c2)) {
    setOf(Color.RED, Color.YELLOW) -> Color.ORANGE
    setOf(Color.YELLOW, Color.BLUE) -> Color.GREEN
    setOf(Color.BLUE, Color.VIOLET) -> Color.INDIGO
    else -> throw Exception("Dirty color")
}

这里使用when结构,包含的是一个set对象

3.4 使用不带参数的“when”

when结构可以不带参数

fun mixOptimized(c1: Color, c2: Color) {
    when {
        (c1 == Color.RED && c2 == Color.YELLOW) ||
                (c1 == Color.YELLOW && c2 == Color.RED) ->
            Color.ORANGE

        (c1 == Color.BLUE && c2 == Color.YELLOW) ||
                (c1 == Color.YELLOW && c2 == Color.BLUE) ->
            Color.GREEN

        (c1 == Color.BLUE && c2 == Color.VIOLET) ||
                (c1 == Color.VIOLET && c2 == Color.BLUE) ->
            Color.INDIGO

        else -> throw Exception("Dirty color")
    }
}

如果没有提供when的参数,分支条件就是任意的布尔表达式

3.5 合并类型检查和转换

Java中,用于判断对象是否是一个类的实例,需要使用instanceof关键字。通常传入的参数是父类类型时,需要转换成子类类型,还需要使用强转。

interface Expr
class Num(val value: Int) : Expr
class Sum(val left: Expr, val right: Expr) : Expr

fun eval(e: Expr): Int {
    if (e is Num) {
        val n = e as Num
        return n.value
    }
    if (e is Sum) {
        return eval(e.left) + eval(e.right)
    }
    throw IllegalArgumentException("Unknown expression")
}

上面是类似于Java中的写法,is关键字用于判断对象是否是Num类对象,相当于instanceof。as关键字用于强转。
但是Kotlin中合并了类型检查和转换

val n = e as Num

这一行是不需要的,因为is关键字在检查的时候,如果返回true,就自动的将e转换成了Num对象

3.6 用“when”代替“if”

上面的例子可以使用when结构进行改造

fun eval(e: Expr): Int =
    when (e) {
        is Num -> e.value
        is Sum -> eval(e.left) + eval(e.right)
        else -> throw IllegalArgumentException("Unknown expression")
    }

直接返回when运算的值,这里的when结构中,用于判断对象类型。

3.7 代码块作为“when”的分支

fun evalWithLogging(e: Expr): Int =
    when (e) {
        is Num -> {
            println("num : ${e.value}")
            e.value
        }

        is Sum -> {
            val left = evalWithLogging(e.left)
            val right = evalWithLogging(e.right)
            println("sum: $left + $right")
            left + right
        }

        else -> throw IllegalArgumentException("Unknown expression")
    }

when结构的分支中可以是代码块。这里由于when直接充当返回值,所以各个分支的最后一行就是返回值

4 循环语句

Kotlin支持两种循环:while循环和for循环

4.1 while循环

语法

while (condition) {
	...
}

do {
	...
} while (condition)

4.2 for循环

Kotlin中的for循环和Java中不太一样,使用的是区间和数列
区间:
表示两个范围内的数,使用..运算符

val oneToTen = 1..10

这个区间是一个闭合的区间,两边临界都能取到

for (i in 0..10) {
    println(i)
}

使用for循环遍历0到10,i不需要提前定义,这里使用的时候就直接定义了

for (i in 0..10 step 2) {
    println(i)
}

可以使用step跳过其中的一些值

for (i in 10 downTo 0 step 2) {
   println(i)
}

可以使用downTo做倒序的遍历

for (i in 0 until 10) {
    println(i)
}
for (i in 0 ..<10) {
    println(i)
}

可以使用until关键字或者..<来做一个左闭右开的区间

4.3 迭代map

循环常用的就是用来遍历集合

val binaryReps = TreeMap<Char, String>()
for (c in 'A'..'F') {
    val binary = Integer.toBinaryString(c.code)
    binaryReps[c] = binary
}
for ((letter, binary) in binaryReps) {
    println("$letter = $binary")
}

map添加元素可以使用使用赋值这种简写方式,遍历的时候可以将key-value展开到两个变量中

val list = arrayListOf("10", "12", "2343")
for ((index, element) in list.withIndex()) {
    println("$index = $element")
}

遍历的时候也可以不用定义索引值,而是通过withIndex获取

4.4 使用“in”检查集合和区间成员

使用in检查元素是否在其中或者!in检查元素是否不在其中

fun isLetter(c: Char) = c in 'a' .. 'z' || c in 'A' .. 'Z'
fun inNotDigit(c: Char) = c !in '0' .. '9'

这两个判断也适用于when表达式

fun recognize(c: Char) = when (c) {
    in '0'..'9' -> "It's a digit"
    in 'a'..'z', in 'A'..'Z' -> "It's a letter"
    else -> "I don't know"
}

5 异常

5.1 抛异常和捕获异常

Kotlin中的异常和Java中相似,一个函数可以正常结束,也可以抛出异常。异常可以处理,也可以向上抛出。
抛出异常:

if (percentage !in 0 .. 100) {
	throw IllegalArgumentException("Invalid percentage")
}

捕获异常

fun readNumber(reader: BufferedReader): Int? {
    try {
        val line = reader.readLine()
        return Integer.parseInt(line)
    } catch (e: NumberFormatException) {
        return null
    } finally {
        reader.close()
    }
}

同样使用try...catch...finally结构

try作为表达式

Kotlin中try可以作为表达式

fun readNumber(reader: BufferedReader): Int? =
    try {
        val line = reader.readLine()
        Integer.parseInt(line)
    } catch (e: NumberFormatException) {
        null
    } finally {
        reader.close()
    }

try是一个表达式,可以直接返回,运算中的最后一行就是返回值

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值