快速上手 Kotlin

前言

其实在刚接触 Kotlin 的时候,我心里是拒绝的,因为看习惯了 Java 的代码,以致于看 Kotlin 代码时有一股极大的别扭感,让我不想去学习(其实在我学 C 语言的时候也是这个感受)。可是没办法呀,谁叫这货是 Android 官方开发语言呢,再加上如今的招聘要求多多少少也要会 Kotlin 了,因而只能硬着头皮上咯。java

1. 基本数据类型

相比较 Java ,Kotlin 没有了基本数据类型,能够看做是 Java 的基本类型包装类拿了过来部分改了名字,以下:程序员

Java Kotlin
byte Byte
short Short
int Int
long Long
float Float
double Double
boolean Boolean
char Char

另外字符串是支持模板的,看下面:数组

fun main() {
    val hello = "Hello"
    println("$hello, World!")
    println("length = ${hello.length}")
}

// 输出:Hello, World!
// length = 5
复制代码

2. 数组

Kotlin 中没有了 [] 进行数组声明或建立,8 种基本数据类型有本身的数组类型,以下:安全

Java Kotlin
byte[] ByteArray
short[] ShortArray
int[] IntArray
long[] LongArray
float[] FloatArray
double[] DoubleArray
boolean[] BooleanArray
char[] CharArray

那引用类型呢?例如咱们要使用一个 String 数组,咱们须要用 Array<T> 这么一个类,泛型表明数组所属类型。同理,基本数据类型也能够用 Array<T> 来声明,如 Array<Int>Array<Boolean> 等等。bash

初始化既能够用它们的构造函数,也能够用 Kotlin 提供的全局函数,arrayOf()emptyArray() ,前者接收一个可变参数,后者则是一个空数组。闭包

3. 访问权限

publicprotectedprivateinternal 做为 4 大访问权限,去除了 defaultide

public 做为默认访问权限,所以不少时候咱们能够不用写上 public 。函数

protected 相较于 Java 不变。学习

private 除了能够修饰类级别,其他不变。为何能够修饰类呢?这是由于 Kotlin 中一个文件能够声明 1 个以上的类,对这些类加上 private 则表示仅能够在当前文件中使用。ui

internal 表示模块级别的访问权限,被 internal 标识则表示仅在当前模块可见,这在模块封装上是颇有帮助的。

4. 继承性

Kotlin 中,类、字段和函数默认是 final 的,抽象类和接口除外。所以若是咱们想要它们变得可继承,须要在前面加上 open 关键字。

对于普通类,若是想要字段或者函数可继承,仅在字段或函数上加 open 是不够的,由于这时候类仍是不可继承的,所以类也须要加上 open ,这时字段和函数才真正可继承。

若是不想一个已 open 的字段或函数可继承,能够用 final 关键字进行修饰。

5. 变量与函数

Kotlin 中将类型进行了后置,什么意思呢?举个栗子:

// Java 中咱们这样声明变量和函数
public final class Person {
	public final String name;
	public int age;
	
	public final void talk(String str) {
		int temp = 0
	}
}

// Kotlin 中等价于
class Person {
	val name: String? = null
	var age: Int = 0
	
	fun talk(str: String?): Unit {
		var temp = 0
	}
}
复制代码

首先 Kotlin 中是能够省略分号的,固然前提是你一个语句一行,若是一行有两个语句的话就不能省略了,正常状况下我们都是一行一语句的,接着来分析上面的代码。

变量

因为类型后置,所以 Kotlin 使用关键字 valvar 来表示变量,val (value) 表示只读变量,在第一次赋值后就不可修改了;var (variable) 就是咱们在 Java 中经常使用的变量了。

在 Kotlin 中,成员变量是没有默认值的,须要咱们手动赋值。

另外,Kotlin 是支持类型推断的,在声明变量时,能够不用写类型,前提是有提供初始值。尽管如此,Kotlin 仍然是强类型语言,类型不匹配照样会报错,例如 var age = 0 ,在这个声明中,尽管咱们没有显式提供类型,但初始值代表了这个变量就是一个 Int 变量(整型默认为 Int),所以后续咱们只能赋 Int 值。

函数

因为类型后置,所以 Kotlin 使用关键字 fun 来表示函数,而且参数列表也遵循类型后置规则,Kotlin 中函数的参数是能够有默认值的,如:

fun setName(name: String = "aaronzzx"): Unit {
	// ...
}
复制代码

这个 : Unit 是啥?Unit 至关于 Java 的 void: Unit 表示返回 Unit 类型,至关于没有返回值,固然更多时候咱们不须要写上这个多余的东西,上面的代码等价于:

fun setName(name: String = "aaronzzx") {
	// ...
}
复制代码

函数也能够进行类型推断,这里指返回值推断,仅在简写函数时可用,举个栗子:

fun getAge(): Int {
	return age;
}

// 等价于
fun getAge() = age

// 当咱们的函数只有 1 个语句时,即便没有返回值,也能够简写。
// 前提这个语句不是赋值语句,否则就 "=" 冲突了
fun function() {
	operate()
}

// 等价于
fun function() = operate()

// 拥有可变参数的函数
fun function(vararg strs: String) {
    for (string in strs) {
        println(string)
    }
}

// 参数为接口或抽象类
fun initView() {
    HttpUtils.request(object: Callback {
        override fun onSuccess() {
            // ...
        }
        
        override fun onFailure() {
            // ...
        }
    })
}
复制代码

Ps:你可能有发现类型后面跟了个 ? ,这个表示此变量可被赋值 null ,若是不加问号是没办法赋值 null 的,这和 Kotlin 的“空安全”有关,后面会说。

6. 构造函数

Kotlin 的构造函数在第一次看到可能会优势懵逼,先来看下与 Java 的对比:

// Java
public class Person {
	private String name;
	private int age;
	
	public Person() {
	
	}
	
	public Person(String name) {
		this.name = name;
	}
	
	public Person(String name, int age) {
		this.name = name;
		this.age = age;
	}
}

// Kotlin
class Person(
	private var name: String? = null,
	private var age: Int = 0
)

// 等价于
class Person(
	name: String? = null,
	age: Int = 0
) {
	private var name: String? = name
	private var age: Int = age
	
	init {
		// 可选
	}
}

// 等价于
class Person() {
	private var name: String? = null
	private var age: Int = 0
	
	constructor(name: String?): this() {
		this.name = name
	}
	
	constructor(name: String?, age: Int): this() {
		this.name = name
		this.age = age
	}
}
复制代码

在这里能够看到,Kotlin 具备碾压性的优点,对于须要重载构造函数,Kotlin 最少可用 4 行代码搞定,接下来分析。

首先 Kotlin 能够省略类体,由于主构造函数被设定在了类名上,主函数能够有参数,也能够没有。若是有参数,能够在参数名前面加关键字 valvar ,表示将这个参数做为成员变量,在关键字前面能够用访问权限或注解进行修饰。

init 表示主构造函数的函数体,若是没有特殊用途能够被省略。

constructor 表示次构造函数,在拥有主构造函数的状况下须要显示调用主构造函数。

7. 空安全

在使用 Java 时,对于 NullPointerException 真的是迫不得已,一不当心就报空指针异常,而后程序就 Crash 了。所以咱们只能常常写以下的判空代码:

public String getMobile(Response response) {
    String mobile = null;
    if (response != null) {
        Data data = response.getData();
        if (data != null) {
            mobile = data.getMobile();
        }
    }
    return mobile;
}
复制代码

或许是知道 Java 程序员处于水深火热之中,Kotlin 添加了空安全这一语言特性,使用 ? 对可空变量进行标识,而没被 ? 标识的变量呢?理所固然的是不会为空了,因而咱们对于不为空的变量就不用写繁琐的判空代码了,由于它不会出现空指针异常了。

对于可为空的变量,咱们仍是须要进行判空的,可是形式不与 Java 同样,因而上面的代码能够这么写:

fun getMobile(response: Response?): String? {
    return response?.data?.mobile
}
复制代码

?. 表示若是当前对象不为空就调用,与 Java 相比,简洁了好多。

8. 伴生对象

companion object ,这是个啥呢?看下面这段代码,你应该就明白了。

class MainActivity : AppCompatActivity() {
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val url = intent.getStringExtra(EXTRA_URL)
        // ...
    }
    
    companion object {
        private const val EXTRA_URL = "EXTRA_URL"
        
        fun start(context: Context, url: String) {
            val intent = Intent(context, MainActivity::class.java)
            intent.putExtra(EXTRA_URL, url)
            context.startActivity(intent)
        }
    }
}
复制代码

说白了,这东西就至关于 Java 的 static ,只是 Kotlin 中没有 static 这个概念,因此若是咱们要声明静态变量或者静态函数,就须要用到 companion object

另外上面的代码,可能有些地方你看起来不太明白,这里说一下。

  • 继承与实现,统统使用 : 操做符,不一样的类或接口用 , 隔开,而且须要显式调用父类构造函数。

  • 首先 Kotlin 中建立对象是省略 new 关键字的,直接用构造函数便可。

  • Java 中的 @Override 注解在 Kotlin 中变成了关键字 override ,重写必不可少。

  • 对于无参的 getter 函数,能够以变量名的形式书写,例如 getIntent() 就是 intent

  • const val 关键字用来声明常量,仅在 objectcompanion object顶层变量 中可用。

  • object 匿名内部类和单例关键字,若是要建立一个单例类,只需这么写。

    object Singleton {
        fun exec() {
            // ...
        }
    }
    
    class Test {
        fun function() {
            // 尽管看起来有点像静态调用,但这就是单例
            Singleton.exec()
        }
    }
    复制代码
  • 顶层变量,用代码比较直观,以下:

    const val TAG = "当前文件"
    
    class MainActivity : AppCompatActivity() {
        // ...
    }
    复制代码

    顶层变量 属于文件,不属于类或接口等等。

9. 分支、循环语句

9.1 分支语句

在 Kotlin 中分支语句属于表达式,所以咱们能够这么写代码:

fun function(type: Int) {
    // if-else
    var str = if (type == 1) {
        "Hello, World!"
    } else if (type == 2) {
        "Goodbye!"
    } else ""
    
    // switch ,在 Kotlin 中关键字变为 when
    str = when (type) {
        1 -> "Hello, World!"
        2 -> "Goodbye!"
        else -> ""
    }
    // 或者 when 不带参数
    str = when {
        type == 1 -> "Hello, World!"
        type == 2 -> "Goodbye!"
        else -> ""
    }
}
复制代码

当分支语句做为表达式时,除非你的条件已经包括全部状况了,不然必定要有 else

9.2 循环语句

whiledo-while 相较于 Java 不变,for 有改动,举个栗子:

fun function() {
    // for (int i = 0; i < 10; i++)
    // 打印的 i 值 是同样的
    for (i in 0..9) {
        println(i)
    }
    for (i in 0 until 10) {
        println(i)
    }
    
    // 下面这段将依次打印 10-0
    for (i in 10 downTo 0) {
        println (i)
    }
    
    // 还能够控制步长
    // 依次打印 0, 2, 4, 6, 8, 10
    for (i in 0..10 step 2) {
        println(i)
    }
}
复制代码

10. 扩展函数

对于任何类型,咱们均可以擅自给他添加新的函数,而后就能够用这个类对象调用咱们自定义的扩展函数了,直接看代码:

fun String.suffix(suffix: String): String {
    return this + suffix
}

fun function() {
    val str = "Hello"
    val newStr = str.suffix(", World!")
    println(str)
    println(newStr)
}

// 将依次输出:Hello
// Hello, World!
复制代码

11. 高阶函数

高阶函数是将函数用做参数或返回值的函数。

在这里咱们会接触到一个东西:闭包, 闭包就是可以读取其余函数内部变量的函数。

class Operator {
    fun add(a: Int, b: Int) = a + b
    fun minus(a: Int, b: Int) = a - b
    
    fun change( a: Int, b: Int, closure: (Int, Int) -> Int
    ): String {
        return "${closure(a, b)}"
    }
}

fun main() {
    val operator = Operator()
    val add: (Int, Int) -> Int = operator::add
    val str1 = operator.change(2, 2, add)
    val str2 = operator.change(5, 2) { a: Int, b: Int ->
        a - b
    }
    println("str1 = $str1")
    println("str2 = $str2")
}

// 输出:str1 = 4
// str2 = 3
复制代码

从 main 函数中,能够看到变量 add 拥有函数类型 (Int, Int) -> Int ,这意味着这个变量只能被函数赋值,对象名::add 表示拿到这个函数的函数类型。

接着看下面的 change 函数,这个大括号里面的代码其实就是闭包,虽然看起来好像是从新定义了一个函数,当一个函数的参数列表最后一个参数是一个闭包时,能够单独将闭包抽出来放在小括号后面,若是参数列表只有一个闭包,那么小括号均可以省略。闭包中,-> 左边表示闭包的参数,类型能够省略。

闭包其实看起来有点像回调,咱们先写好代码,而后在 change 函数中进行调用。

函数类型能够没有参数,可是小括号是不能省略的,包括返回类型也不可省略,例如一个没有参数没有返回值的函数类型,能够这样表示:() -> Unit

另外函数类型也是可空的,举个栗子:

fun main() {
    // 当加上?时,函数类型须要用括号括起来
    val function: (() -> Unit)? = {
        // ...
    }
    
    // 调用时须要用 invoke 进行调用
    function?.invoke()
}
复制代码
相关文章
相关标签/搜索