kotlin是 JetBrains 在 2010 年推出的基于 JVM 的新编程语言。开发者称,设计它的目的是避免 Java 语言编程中的一些难题。比如:在 Kotlin 中类型系统控制了空指针引用,可以有效避免 Java 中常见的NullPointException。
作为一个跨平台的语言,Kotlin 可以工作于任何 Java 的工作环境:服务器端的应用,移动应用(Android版),桌面应用程序。
Kotlin的优势
相比于 Java,Kotlin 有着更好的语法结构,安全性和开发工具支持。Kotlin 中没有基础类型,数组是定长的,泛型是安全的,即便运行时也是安全的。此外,该语言支持闭包,还可通过内联进行优化。不过,它不支持检查异常(Checked Exceptions),许多语言设计者认为这是它的瑕疵。不论如何,重要的是 Java 和 Kotlin 之间的互操作性:Kotlin 可以调用 Java,反之亦可。
变量
fun main(args: Array<String>) {
var quantity = 5
val price: Double = 20.3
val name: String = "大米"
println("单价:$price")
println("数量:$quantity")
println("产品:$name 总计:${quantity * price}")
}
in关键字的使用
//如果存在于区间(1,Y-1),则打印OK
if (x in 1..y-1)
print("OK")
//如果x不存在于array中,则输出Out
if (x !in 0..array.lastIndex)
print("Out")
//打印1到5
for (x in 1..5)
print(x)
//遍历集合(类似于Java中的for(String name : names))
for (name in names)
println(name)
//如果names集合中包含text对象则打印yes
if (text in names)
print("yes")
when表达式
类似于 Java 中的 switch,但是 Kotlin 更加智能,可以自动判断参数的类型并转换为响应的匹配值。
fun cases(obj: Any) {
when (obj) {
1 -> print("第一项")
"hello" -> print("这个是字符串hello")
is Long -> print("这是一个Long类型数据")
!is String -> print("这不是String类型的数据")
else -> print("else类似于Java中的default")
}
}
智能类型推测
判断一个对象是否为一个类的实例,可以使用is关键字
与 Java 中的instanceof关键字类似,但在 Kotlin 中如果已经确定了一个对象的类型,可以在接下来的代码块中直接作为这个确定类型使用。
fun getStringLength(obj: Any): Int? {
if (obj is String) {
// 做过类型判断以后,obj会被系统自动转换为String类型
return obj.length
}
//同时还可以使用!is,来取反
if (obj !is String){
}
// 代码块外部的obj仍然是Any类型的引用
return null
}
空值检测
Kotlin 是空指针安全的,也就意味着你不会再看到那恼人的空指针异常。
例如这句代码 println(files?.size),只会在files不为空时执行。
以及,你可以这样写
//当data不为空的时候,执行语句块
data?.let{
//...
}
//相反的,以下代码当data为空时才会执行
data?:let{
//...
}
函数
函数的声明
函数使用关键字fun声明,如下代码创建了一个名为say()的函数,它接受一个String类型的参数,并返回一个String类型的值
fun say(str: String): String {
return str
}
同时,在 Kotlin 中,如果像这种简单的函数,可以简写为
fun say(str: String): String = str
如果是返回Int类型,那么你甚至连返回类型都可以不写
fun getIntValue(value: Int) = value
函数的默认参数
你也可以使用默认参数来实现重载类似的功能
fun say(str: String = "hello"): String = str
这时候你可以调用say(),来得到默认的字符串 "hello",也可以自己传入参数say("world")来得到传入参数值。
有时参数非常多的时候,也可以使用多行参数的写法,它们是相同的
fun say(firstName: String = "Tao",
lastName: String = "Zhang"){
}
变参函数
同 Java 的变长参数一样,Kotlin 也支持变长参数
//在Java中,我们这么表示一个变长函数
public boolean hasEmpty(String... strArray){
for (String str : strArray){
if ("".equals(str) || str == null)
return true;
}
return false;
}
//在Kotlin中,使用关键字vararg来表示
fun hasEmpty(vararg strArray: String?): Boolean{
for (str in strArray){
if ("".equals(str) || str == null)
return true
}
return false
}
扩展函数
你可以给父类添加一个方法,这个方法将可以在所有子类中使用。例如,在 Android 开发中,我们常常使用这样的扩展函数:
fun Activity.toast(message: CharSequence, duration: Int = Toast.LENGTH_SHORT) {
Toast.makeText(this, message, duration).show()
}
将函数作为参数
Kotlin 中,可以将一个函数作为参数传递给另一个函数
fun invokeModule(webview: View?, cmd: String, parameters: String,
callback: String,
callbackFunction: (String, String) -> Unit):String?
上面的代码中,我们传入了一个(String, String) -> Unit的参数
kotlin类特性
与 Java 相同,Kotlin 声明类的关键字是class。类声明由类名、类头和类体构成。
其中类头和类体都是可选的; 如果一个类没有类体,那么花括号也是可以省略的。
构造函数
Kotlin 的构造函数可以写在类头中,跟在类名后面,如果有注解还需要加上关键字constructor。这种写法声明的构造函数,我们称之为主构造函数。例如下面我们为Person创建带一个String类型参数的构造函数。
class Person(private val name: String) {
fun sayHello() {
println("hello $name")
}
}
在主构造函数中不能有任何代码实现,如果有额外的代码需要在构造方法中执行,你需要放到init代码块中执行。
class Person(private var name: String) {
init {
name = "Zhang Tao"
}
internal fun sayHello() {
println("hello $name")
}
}
protected 在 “top-level” 中不可以使用,即不能修饰包级别的方法或者属性等
private 声明在包含声明的源文件中可见
internal 声明,在同一模块中的任何地方可见
次构造函数
class Person(private var name: String) {
private var description: String? = null
init {
name = "Zhang Tao"
}
constructor(name: String, description: String) : this(name) {
this.description = description
}
internal fun sayHello() {
println("hello $name")
}
}
枚举类
enum class Programer {
JAVA, KOTLIN, C, CPP, ANDROID;
}
密封类
sealed class BaseClass {
class Test1 : BaseClass() {
override fun test() {
println("Test1实例")
}
}
class Test2 : BaseClass() {
override fun test() {
println("Test2实例")
}
}
object Test3 : BaseClass() {
override fun test() {
println("Test3实例")
}
}
open fun test() {
println("BaseClass实例")
}
}
sealed 修饰的类称为密封类,用来表示受限的类层次结构。例如当一个值为有限集中的 类型、而不能有任何其他类型时。在某种意义上,他们是枚举类的扩展:枚举类型的值集合也是受限的,但每个枚举常量只存在一个实例,而密封类的一个子类可以有可包含状态的多个实例。
data 数据类
data 修饰的类称之为数据类。它通常用在我们写的一些 POJO 类上。当 data 修饰后,会自动将所有成员用operator声明,即为这些成员生成类似 Java 的 getter/setter 方法。
编译器自动从主构造器中的属性导入下面这些成员函数:
- equals()/hashCode()
- toString()(形式为User(name=John,age=42))
- componentN()函数对应着声明的属性顺序
- copy()函数
data class User(val name: String, val age: Int)
伴生对象
由于 Kotlin 没有静态方法。在大多数情况下,官方建议是简单地使用 包级 函数。如果你需要写一个可以无需用一个类的实例来调用、但需要访问类内部的函数(例如,工厂方法或单利),你可以把它写成一个用 companion修饰的对象内的方法。我们称companion修饰的对象为伴生对象。
class StringUtils {
companion object {
fun isEmpty(str: String): Boolean {
return "" == str
}
}
}
单例类的设计
class Single private constructor() {
companion object {
fun get():Single{
return Holder.instance
}
}
private object Holder {
val instance = Single()
}
}
通过关键字object
object Single{
}
委托
Kotlin 直接支持委托模式,更加优雅,简洁。Kotlin 通过关键字 by 实现委托。有点像java的动态代理。
interface Animal{
fun bark()
}
class Dog :Animal {
override fun bark() {
println("Wang Wang")
}
}
class Cat(animal: Animal) : Animal by animal {
}
fun main(args: Array<String>) {
Cat(Dog()).bark()
}
这样,我们就很成功的让一只猫的叫声用狗去代理掉了,于是上面的main方法执行完后就变成了 Wang Wang。
属性的委托
import kotlin.reflect.KProperty
// 定义包含属性委托的类
class Example {
var p: String by Delegate()
}
// 委托的类
class Delegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return "$thisRef, 这里委托了 ${property.name} 属性"
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
println("$thisRef 的 ${property.name} 属性赋值为 $value")
}
}
fun main(args: Array<String>) {
val e = Example()
println(e.p) // 访问该属性,调用 getValue() 函数
e.p = "Runoob" // 调用 setValue() 函数
println(e.p)
}
输出结果:
Example@433c675d, 这里委托了 p 属性
Example@433c675d 的 p 属性赋值为 Runoob
Example@433c675d, 这里委托了 p 属性
可观察属性 Observable
@[Kotlin 的标准库中已经内置了很多工厂方法来实现属性的委托。]
import kotlin.properties.Delegates
class User {
var name: String by Delegates.observable("初始值") {
prop, old, new ->
println("旧值:$old -> 新值:$new")
}
}
fun main(args: Array<String>) {
val user = User()
user.name = "第一次赋值"
user.name = "第二次赋值"
}
执行输出结果:
旧值:初始值 -> 新值:第一次赋值
旧值:第一次赋值 -> 新值:第二次赋值
闭包
一段程序代码通常由常量、变量和表达式组成,然后使用一对花括号“{}”来表示闭合,并包裹着这些代码,由这对花括号包裹着的代码块就是一个闭包。其实在签名我们也介绍了全局和嵌套函数就是一种特殊的闭包。这里,我们总结了一下,Kotlin语言中有三种闭包形式:全局函数、自嵌套函数、匿名函数体。
fun main(args: Array<String>) {
// 执行test闭包的内容
test
}
// 定义一个比较测试闭包
val test = if (5 > 3) {
println("yes")
} else {
println("no")
}
闭包的用途?
/**
* 计数统计
*/
fun justCount():() -> Unit{
var count = 0
return {
println(count++)
}
}
fun main(args: Array<String>) {
val count = justCount()
count() // 输出结果:0
count() // 输出结果:1
count() // 输出结果:2
}
自执行闭包
自执行闭包就是在定义闭包的同时直接执行闭包,一般用于初始化上下文环境。 例如:
{ x: Int, y: Int ->
println("${x + y}")
}(1, 3)
Lambda表达式
val printMsg = { msg: String ->
println(msg)
}
fun main(args: Array<String>) {
printMsg.invoke("hello")
}
带接收者的with,apply
var s=with("abc"){
this.plus("d")
}
println(s)
apply始终返回座位实参传递给它的对象
var r="abc".apply {
this.plus("d")
}
println(r)
let表达式一般结合类型安全检查使用
var x: String? = null
x?.let {
println("x=${it}")
}
集合和泛型
所有类声明的泛型尖括号里面如果加入了 out 关键字,则说明这个类的对象是只读的,例如他只有:get()、size()等方法,而没有 set()、remove()等方法。
相反的,如果没有加 out 关键字,或者换一种记法:如果开头是 MutableXXX 那就是一个跟 Java 中用法一致的集合类。
open class A
open class B : A()
open class C : B()
class TypeArray<in A> {
//in 修饰了 A,表示 A 是可以作为参数的。
fun getValue(a: A): Int? {
return a?.hashCode()
}
//这段代码是非法的,因为A 不能被返回
fun getA(a: A): A? {
return a
}
}
集合的初始化
在 Kotlin 中,集合类一般不使用构造方法去初始化,而是使用同一的入口方法,例如初始化一个 MutableList,我们使用的是如下代码:
val mutableList = mutableListOf(0, 1, 2, 3)
类似的初始化集合对象的方法还有
//创建一个 List<> 对象
var list = listOf(0, 1, 2)
//创建一个 Set<> 对象
val ss = setOf(1, 2, 4)
操作符
比如说输出wifi密码duowan123
java实现:
final String[] a = new String[]{"a", "1", "u", "2", "o", "n", "3", "d", "w"};
final Integer[] indexs = new Integer[]{7, 10, 2, 4, 11, 8, 0, 5, 1, 3, 15, 6};
常用操作符
Kotlin 的操作符跟 RxJava 基本一致,不需要额外记忆。
下标操作类
- contains —— 判断是否有指定元素
- elementAt —— 返回对应的元素,越界会抛- IndexOutOfBoundsException
- firstOrNull —— 返回符合条件的第一个元素,没有 返回null
- lastOrNull —— 返回符合条件的最后一个元素,没有 返回null
- indexOf —— 返回指定元素的下标,没有 返回-1
- singleOrNull —— 返回符合条件的单个元素,如有没有符合或超过一个,返回null
判断类
- any —— 判断集合中 是否有满足条件 的元素
- all —— 判断集合中的元素 是否都满足条件
- none —— 判断集合中是否 都不满足条件,是则返回true
- count —— 查询集合中 满足条件 的 元素个数
- reduce —— 从 第一项到最后一项进行累计
过滤类
- filter —— 过滤 掉所有 满足条件 的元素
- filterNot —— 过滤所有不满足条件的元素
- filterNotNull —— 过滤NULL
- take —— 返回前 n 个元素
转换类
- map —— 转换成另一个集合(与上面我们实现的 convert 方法作用一样);
- mapIndexed —— 除了转换成另一个集合,还可以拿到Index(下标);
- mapNotNull —— 执行转换前过滤掉 为 NULL 的元素
- flatMap —— 自定义逻辑合并两个集合;
- groupBy —— 按照某个条件分组,返回Map;
排序类
- reversed —— 反序
- sorted —— 升序
- sortedBy —— 自定义排序
- sortedDescending —— 降序
用于 Android 开发的工具
Kotlin 团队为 Android 开发提供了一套超越标准语言功能的工具:
- Kotlin Android 扩展是一个编译器扩展, 可以让你摆脱代码中的 findViewById() 调用,并将其替换为合成的编译器生成的属性。
- Anko 是一个提供围绕 Android API 的 Kotlin 友好的包装器的库 ,以及一个可以用 Kotlin 代码替换布局 .xml 文件的 DSL。
- Android KTX 一个 Kotlin 代码的扩展库,比较常用的一些代码块,进行封装,然后在这个基础上,提供更良好的 API,供开发者使用。
参考链接