博客主页java
Kotlin核心语法(二):kotlin程序结构、函数
Kotlin核心语法(三):kotlin类、对象、接口
Kotlin核心语法(四):kotlin Lambda编程git
Kotlin就是一门能够运行在Java虚拟机、Android、浏览器上的静态语言,它与JAVA 100%兼容,若是你对Java很是熟悉,那么你就会发现Kotlin除了本身的标准库以外,大多仍然使用经典的JAVA集合框架。github
先来体验一下Kotlin代码。Book类包含两个属性:name 和 price。 price属性默认值为null,算法
// 可空类型(Float?) price的实参默认值为null data class Book(val name: String, val price: Float? = null) // 入口函数 fun main(args: Array<String>) { val books = listOf(Book("Kotlin"), Book("Java", 13.9f)) // lambda表达式, maxBy查找books列表中价格最贵的书 // 若是price属性值为null,Elvis云算法(?:)会返回零 val book = books.maxBy { it.price ?: 0.0f } println("the book is :$book") } // 自动生成Book的toString方法 // the book is :Book(name=Java, price=13.9)
Kotlin具备类型推导能力,在源代码中也不用显示地声明每一个变量的类型,会根据上下文自动判断,val x = 1, 会自动判断变量x的类型是Int
Kotlin对可空类型的支持,在编译期检测可能存在的空指针异常编程
http://github.com/jetbrains/k...
Kotlin团队打造的库Anko(http://github.com/kotlin/anko)给不少标准Android API 添加了Kotlin友好的适配器。segmentfault
Kotlin 和 Java 都是编译型语言,必须先编译,而后才能执行代码。数组
Kotlin官方网站
https://kotlinlang.org/docs/t...
Kotlin源代码存放在后缀名为.kt的文件中。Kotlin编译器会分析源代码并生成.class文件,而后执行生成的.class文件浏览器
$ kotlinc hello.kt -include-runtime -d hello.jar $ java -jar hello.jar
// HelloWord.kt // 1. 使用关键字fun声明一个函数 // 2. 参数的类型写在参数名后面,args: Array<String> // 3. 函数能够定义在文件的最外层,不须要把它放在类中 // 4. 数组就是类,在Kotlin中没有声明数组类型的特殊语法,如:Array<String> // 5. 使用println代替System.out.println。Kotlin标准库给JAVA标准库函数提供不少语法更简洁的包装 // 6. 能够省略每行代码结尾的分号 fun main(args: Array<String>) { println("Hello, Word!") }
声明一个有返回值的函数,参数列表的后面跟着返回值类型,用 冒号(:)隔开安全
// 声明格式: fun 函数名(参数列表) : 返回类型 { 函数体 } fun max(a: Int, b: Int): Int { // 在Kotlin中,if 是表达式,而不是语句。表达式有值,且能做为另外一个表达式的一部分是使用 return if (a > b) a else b } // 若是函数体是单个表达式,这个表达式能够做为完整的函数体,且可省略花括号 和 return 语句 fun max2(a: Int, b: Int): Int = if (a > b) a else b // 也能够省略 返回值类型。注意:只有表达式体函数的返回类型能够省略 fun max3(a: Int, b: Int) = if (a > b) a else b
变量标识一个对象的地址,称为标识符。在Kotlin中,全部变量类型都是引用类型。服务器
Kotlin中,变量又分为 可变变量(var) 和 不可变量(val)
默认状况下,尽量使用 val 关键字声明全部的Kotlin变量。
几个注意点:
val message: String if (条件) { message = "Success" } else { message = "Failed" }
val books = arrayListOf("Kotlin") books.add("Java")
var age = 23 // 错误:类型不匹配 age = "32"
Kotlin中,变量声明以关键字(val、var)开始,而后变量名。最后加上变量类型(也能够不加,编译器会自动类型推导)
val a: Int = 12 // 也能够省略变量类型,编译器会分析初始化表达式的值,并把它的类型做为变量的类型 val a = 12 // 若是变量没有初始化,须要显示地指定它的类型 val b: Int b = 13
基本数据类型与引用数据类型在建立时,内存存储方式区别:
Java中每个基本数据类型都引入了对应的包装类型(wrapper class),如:int 的包装类型就是 Integer,从Java 5 开始引入自动装箱、拆箱机制。
Kotlin中去掉了原始类型,只有包装类型,编译器在编译代码的时候,会自动优化性能,把对应的包装类型拆箱为原始类型。
Kotlin是不区分基本数据类型和它们的包装类。如:Int
val a: Int = 12 val list: List<Int> = listOf(11, 12, 13)
Kotlin还能够对数字类型的值调用方法,coerceIn是标准库函数,把值限制在特定范围内
val a: Int = 110 val newValue = a.coerceIn(0, 100) println("把值限制在0到100之间, 限制前的值: $a, 限制后的值: $newValue") // 把值限制在0到100之间, 限制前的值: 110, 限制后的值: 100
Koltin的Int类型会被编译成Java基本数据类型int,除泛型类外,泛型类型参数的基本数据类型会被编译成对应的Java包装类型,如Int类型编译成java.lang.Integer
在Kotlin中,不可空基本数据类型与Java中的原始的数字类型对应,如:Kotlin中Int,对应Java中的int;
可空的基本数据类型与Java中的装箱类型对应,如:Kotlin中Int?,对应Java中Integer
null 只能被存储在Java引用类型的变量中,Kotlin中可空数基本据类型不能用Java的基本数据类型表示。
// 使用可空基本数据类型(Int?)变量a,会被编译成java.lang.Integer类型 fun isGreaterThan5(a: Int?): Boolean? { if (a == null) return null return a > 5 }
Kotlin不会自动把数字从一种类型转换成另外一种类型,如:Int类型不会自动转换为Long类型
val a: Int = 12 // 这行代码报错:类型不匹配 val b: Long = a // 须要显示的进行转换 val b: Long = a.toLong()
Koltin要求转换必须是显示的,尤为在比较装箱值的时。比较两个装箱值的equals方法不只会检查它们存储的值,还要比较装箱类型。在Java中new Integer(12).equals(new Long(12)) 返回false。
val a: Int = 12 val list = listOf(12L, 13L, 14L) // 这行代码编译器不会编译,要求显示的转换类型 a in list // 显示的将 Int 转换 Long 才能够比较 a.toLong() in list
Kotlin 标准库提供不少扩展方法,如:字符串转换成基本数据类型(toInt,toByte,toBoolean等),若是转换失败抛出 NumberFormatException
println("12".toInt())
Java的超类型是Object,Kotlin的全部非空类型的超类型是Any类型。可是:在Java中,Object只是全部引用类型的超类型(引用类型的根),而基本数据类型并非。在Kotlin中,Any是全部类型的超类型,包括Int基本数据类型。
// 基本数据类型的值赋值给Any类型的变量时会自动装箱 // Any是非空类型,不能持有null值,若是持有任何可能值,包括null,必须使用 Any? 类型 val a: Any = 12 // Kotlin中使用Any类型会编译转换成java.lang.Object
全部Kotlin类都包含下面三个方法:equals、hashCode、toString。这三个方法定义在Any类中,可是Any类不能使用其余java.lang.Object的方法(如:wait、notify),能够转换成java.lang.Object来调用这些方法。
Kotlin中若是函数没有返回值时,可使用Unit做为函数返回类型
fun f(): Unit { ... } // Unit 能够省略 fun f(){ ... }
Kotlin的Unit 和 Java的 void区别:Unit能够做为类型参数,而void不行。当只存在一个值是Unit类型,这个值也叫做Unit,且在函数中会被隐式的返回。
interface Processor<T> { fun process(): T } class NoResultProcessor : Processor<Unit> { // 返回Unit类型,可是能够省略 override fun process() { // 不须要显式的return,编译器会隐式地加上return Unit } }
在Java中,为了解决使用 没有值 做为类型参数,给出的方案没有Kotlin好。一种是选择使用分开的接口定义来分别表示须要和不须要返回值的接口(如:Callable 和 Runnable),另外一种是用特殊的 java.lang.Void 类型做为类型参数,但仍是须要加入一个return null;语句
Nothing类型没有任何值,只有被看成函数返回值使用,或者被看成泛型函数返回值的类型参数使用才有意义。
Kotlin对 可空类型 是显示支持的。如:String?、Int?,能够存储 null 引用。没有问号的类型表示这种类型不能存储null引用,说明默认都是非空的类型,除非显示的把它标记为可空类型。
// 下面是一段java代码 // 若是调用函数时传入null,将抛出NullPointerException int strLen(String s) { return s.length(); } // 下面是一段Kotlin代码 // 若是调用函数时传入null,kotlin编译器是不容许的,保证了strLen函数永不会抛出NullPointerException // 编译期会标记成错误:Null can not be a value of a non-null type String fun strLen(s: String): Int = s.length
上例中,若是在调用strLen函数的时容许传入null,须要显示的在参数类型后面加上问号(?)标记
// ? 能够加在任何类型的后面来表示这个类型的变量能够存储null引用 fun strLenSafe(s: String?): Int = ... // 下面这段Kotlin代码,s.length 编译器是不容许的 // ERROR: only safe (?) or non null asserted (!!.) calls are allowed // on a nullable receiver of type kotlin.String? // 可使用 if 检查处理可控性,可是代码就会变冗长。但Kotlin提供了更简洁的方式处理可空值 fun strLenSafe(s: String?): Int = s.length // 下面这段Kotlin代码,编译器也是不容许的,不能赋值给非空类型的变量 // ERROR: Type mismatch:Required String , Found String? val a: String? = null val b: String = a
安全调用运算符:?. 容许把null的检查和方法调用合并一个操做。
string?.length() 等价于 if (string != null) string.length() else null
安全调用运算符:?. 调用的结果类型也是可空的,下面例子中,s?.toUpperCase()结果类型是String?
fun printAllCaps(s: String?) { // allCaps 多是null val allCaps: String? = s?.toUpperCase() println(allCaps) } >>> printAllCaps(null) null
多个安全调用运算符能够连接调用
class Address(val name: String) class Company(val name: String, val address: Address?) class Person(val name: String, val company: Company?) fun Person.printAddress(): String? { // 多个安全调用运算符连接调用 val addressName = this.company?.address?.name // Elvis运算符:?: addressName?:"Unknown" return if (addressName != null) addressName else "Unknown" } >>> val person = Person("kerwin", null) >>> println(person.printAddress()) Unknown
Kotlin使用Elvis运算符(或者null合并运算符)来提供代替 null 的默认值。
Elvis运算符?: 和 安全调用运算符?.一块儿使用
// 当s==null, s?.length 返回null // s?.length == null 返回 0 fun strLenSafe(s: String?): Int? = s?.length ?: 0 >>> println(strLenSafe("abc")) 3 >>> println(strLenSafe(null)) 0
as? 运算符尝试把值转换成指定的类型,若是值不是合适的类型就返回null
class Person(val firstName: String, val lastName: String) { override fun equals(other: Any?): Boolean { // 若是other不是Person类型,other as? Person 返回null,就会直接返回false val otherPerson = other as? Person ?: return false return otherPerson.firstName == firstName && otherPerson.lastName == lastName } } >>> val p1 = Person("Kerwin", "Tom") >>> val p2 = Person("Kerwin", "Tom") >>> println(p1 == p2) true
非空断言使用 双感叹号(!!)表示,能够把任何值转换成非空类型,若是对null值作非空断言,则会抛出异常。
fun ignoreNulls(s: String?) { // 若是s == null, 抛出KotlinNullPointerException val sNotNull = s!! println(sNotNull.length) } >>> ignoreNulls(null) Exception in thread "main" kotlin.KotlinNullPointerException
和安全调用运算符一块儿使用,容许对表达式求值,检查求值结果是否为null,并把结果保存为一个变量。
let函数只在值非空时才被调用
fun sendMessage(message: String) { println(message) } // 当 message != null时,才会执行lambda表达式 >>> val message: String? = null >>> message?.let { msg -> sendMessage(msg) } >>> // it 默认变量名,能够简写 message?.let { sendMessage(it) }
不少框架会对对象实例建立以后用专门的方法来初始化对象。如:Activity的初始化发生在onCreate方法中,JUnit要求把初始化逻辑放在用 @Before注解的方法中。
class MyService{ fun performAction(): String = "test" } class MyTest { // 声明一个可空类型的属性并初始化为null private var myService: MyService? = null @Before fun setup() { // 在setup方法中提供真正的初始化器 myService = MyService() } @Test fun testAction() { // 必须注意可空性:要么用 !!,要么用 ?. Assert.assertEquals("test", myService!!.performAction()) } }
上例kotlin代码中,对属性myService每一次访问都须要可空性判断,Kotlin为了解决这个问题,能够把属性myService声明成能够延迟初始化,使用 lateinit 修饰符
class MyService{ fun performAction(): String = "test" } class MyTest { // 使用 lateinit 声明一个不须要初始化器的非空类型的属性 // 延迟初始化的属性都是var // 若是在属性初始化以前就访问了它,抛出异常:lateinit property myService has not been initialized private lateinit var myService: MyService @Before fun setup() { myService = MyService() } @Test fun testAction() { // 不须要 null 检查直接访问属性 Assert.assertEquals("test", myService.performAction()) } }
为可空类型定义扩展函数是一种更强大的处理null值的方式。Kotlin标准库中定义的 String 的两个扩展函数 isEmpty 和 isBlank 。函数isEmptyOrNull 和 isNullOrBlank 就能够由 String? 类型的接受者调用
fun verifyUserInput(input: String?) { // 可空类型的值.可空类型的扩展 if (input.isNullOrBlank()){ // 不须要安全调用 println("input is null or blank.") } } >>> verifyUserInput(null) input is null or blank. // 函数isNullOrBlank实现,可空字符串的扩展 // return this == null || this.isBlank()
Kotlin中全部泛型类和泛型函数的类型参数默认都是可空的。任何类型,包括可空类型在内,均可以替换类型参数。使用类型参数做为类型的声明都容许为 null,尽管类型参数T并无问号结尾
fun <T> printHashCode(t: T) { // 由于 t 可能为null,因此必须使用安全调用,尽管没有问号结尾,实参t仍容许持有null println(t?.hashCode()) } // T 被推导成 Any? >>> printHashCode(null) null
要使类型参数非空,必需要为它指定一个非空的上界
// 如今 T 就不是可空的 fun <T: Any> printHashCode(t: T) { println(t.hashCode()) } // 编译器不容许的,不能传递null,由于指望值是非空值 >>> printHashCode(null) Null can not be a value of a non-null type TypeVariable(T)
Java中使用注解表达可空性,如 @Nullable String 被Kotlin看成 String?,而 @NotNull String被Kotlin看成 String
Kotlin中的集合库是已Java为基础构建的,并经过扩展函数增长的特性来加强它。
Kotlin支持类型参数的可空性。可是要当心决定什么是可空的:集合的元素仍是集合自己
// List<Int?>能持有Int?类型值的列表,也就是说持有 Int 或者 null fun readNumbers(reader: BufferedReader): List<Int?> { // 建立包含可空Int值的列表 val result = ArrayList<Int?>() for (line in reader.lineSequence()) { println("line: $line") try { // 向列表添加非空值整数 val number = line.toInt() result.add(number) } catch (e: NumberFormatException) { // 解析失败,向列表中添加null值 result.add(null) } } return result }
在使用可空值的集合时,须要使用null检查
// List<Int?>? 声明一个变量持有可空的列表,且包含空的数字 // List<Int?> 声明一个变量不为null的列表,且包含空的数字 fun addValidNumbers(numbers: List<Int?>) { var sumOfValidNumbers = 0 var invalidNumbers = 0 for (number in numbers) { // 检查是否为null if (number != null) { sumOfValidNumbers += number } else { invalidNumbers++ } } println("sum of valid numbers:$sumOfValidNumbers") println("Invalid numbers:$invalidNumbers") } // 可使用Kotlin提供的标准库函数filterNotNull()来完成的,遍历一个包含可空值的集合并过滤掉null // 可是filterNotNull()返回的集合类型,不会在包含任何为null的元素,因此返回集合类型如:List<Int>
Kotlin中把访问集合数据的接口和修改集合数据的接口分开了。通常规则:在代码的任何地方都应该使用只读接口,在代码须要修改集合的地方使用可变接口的变体。 可是不能把只读集合类型的变量赋值给可变的集合变量。
从kotlin.collections.Collection接口中能够看出:能够遍历集合中的元素、获取集合大小、判断集合中是否包含某个元素。这个接口没有任何添加或者移除元素的方法。
public interface Collection<out E> : Iterable<E> { public val size: Int public fun isEmpty(): Boolean public operator fun contains(element: @UnsafeVariance E): Boolean override fun iterator(): Iterator<E> public fun containsAll(elements: Collection<@UnsafeVariance E>): Boolean }
kotlin.collections.MutableCollection接口能够修改集合中的数据。
public interface MutableCollection<E> : Collection<E>, MutableIterable<E> { override fun iterator(): MutableIterator<E> public fun add(element: E): Boolean public fun remove(element: E): Boolean public fun addAll(elements: Collection<E>): Boolean public fun removeAll(elements: Collection<E>): Boolean public fun retainAll(elements: Collection<E>): Boolean public fun clear(): Unit }
注意:只读集合并不老是线程安全的。
集合建立函数
集合类型 | 只读 | 可变 |
---|---|---|
List | listOf | mutableListOf、arrayListOf |
Set | setOf | mutableSetOf、 hashSetOf、linkedSetOf、sortedSetOf |
Map | mapOf | mutableMapOf、hashMapOf、linkedMapOf、sortedMapOf |
默认状况下,应该优先使用集合,而不是数组。
Kotlin中数组是一个带有类型参数的类,其元素类型被指定为相应的类型参数
fun printArray(args: Array<String>) { // 使用扩展属性args.indices,在下标的范围内迭代 for (i in args.indices) { // 经过下标使用array[index]访问元素 println("Argument $i is ${args[i]}") } }
在Kotlin中建立数组:
// 使用Array构造函数建立数组,能够省略数组元素的类型 val letters = Array<String>(26) { // lambda表达式接收数组元素的下标并返回放在数组下标位置的值 i -> ('a' + i).toString() } println(letters.joinToString("")) //abcdefghijklmnopqrstuvwxyz
Kotlin最多见的建立数组的状况之一是调用参数为数组的Java方法,或者调用带有vararg参数的Kotlin函数。
// 向vararg方法传递集合 val strings = listOf("a", "b", "c") // fun String.format(vararg args: Any?): String // 指望vararg参数时,使用展开运算符(*)传递数组 // 使用toTypedArray方法将集合转换为数组 println("%s/%s/%s".format(*strings.toTypedArray())) // a/b/c
Kotlin提供了若干独立的类表示基本数据类型的数组,如:Int类型值的数组叫做IntArray,还提供ByteArray、CharArray、BooleanArray等。他们对应Java基本数据类型数组,如:int[]、byte[]、char[]。这些数组中值存储时没有装箱,最高效。
在Kotlin建立基本数据类型的数组:
// 建立存储5个0的整数数组 一、 val arr1 = IntArray(5) 二、 val arr2 = intArrayOf(0, 0, 0, 0, 0) 三、 val arr3 = IntArray(5) { i -> 0 } public fun intArrayOf(vararg elements: Int): IntArray public class IntArray(size: Int) { public inline constructor(size: Int, init: (Int) -> Int) }
若是个人文章对您有帮助,不妨点个赞鼓励一下(^_^)