kotlin language主页: http://kotlinlang.org/
kotlin在线编程网址: https://try.kotlinlang.org/#/Examples/Hello,%20world!/Simplest%20version/Simplest%20version.kt
kotlin官方文档:http://kotlinlang.org/docs/reference/
继上一个kotlin的插件下载配置后这是第二篇kotlin的文章,(再次说明一下,Android studio不是3.0或以上版本才是以插件的方式配置kotlin的,3.0版本就不用这样折腾了,看kotlin官网说明即知):
Tools for Android Development
The Kotlin team offers a set of tools for Android development that goes beyond the standard language features:
Kotlin Android Extensions is a compiler extension that allows you to get rid of findViewById() calls in your code and to replace them with synthetic compiler-generated properties.
Anko is a library providing a set of Kotlin-friendly wrappers around the Android APIs, as well as a DSL that lets your replace your layout .xml files with Kotlin code.
Next Steps
Download an install Android Studio 3.0 Preview, which includes Kotlin support out of the box.
Follow the Getting Started with Android and Kotlin tutorial to create your first Kotlin application.
For a more in-depth introduction, check out the reference documentation on this site and Kotlin Koans.
Another great resource is Kotlin for Android Developers, a book that guides you step by step through the process of creating a real Android application in Kotlin.
Check out Google's sample projects written in Kotlin.
好了,今天的任务是熟悉基本语法,并完成一个小应用。
Basic Syntax
一 defining packages 包结构的定义
Package specification should be at the top of the source file:
package my.demo
import java.util.*
// ...
It is not required to match directories and packages: source files can be placed arbitrarily in the file system.
See Packages.
包的引入必须是放在代码文件的最顶端的,这一点和Java么有什么差别。后面那句意思是,目录和包没有必要一定要匹配,代码文件可以放在文件系统的任意位置,我觉得这个意思就是说在代码文件当中引入packages的时候是不会受到路径制约的,其实就是Android studio环境下面能够自动根据引入的包来找到对应的资源。最后有一个进入到packages语法的链接,索性过去先看看packages部分的语法和Java有什么区别吧:
Packages
A source file may start with a package declaration:
package foo.bar
fun baz() {}
class Goo {}
// ...
All the contents (such as classes and functions) of the source file are contained by the package declared.So, in the example above, the full name ofbaz()
is foo.bar.baz
, and the full name of Goo
isfoo.bar.Goo
.
If the package is not specified, the contents of such a file belong to "default" package that has no name.
代码文件里面的函数或者类,是引入的包里面的资源,如果没有在前面引入这个包,那么在代码的函数和类里面就要写上“全称“了,即,上述举例当中如果没有引入对应的packages就应该在代码当中写为:foo.bar.baz(),或者类的形式为:foo.bar.Goo,这样子了。这也说明了第一句话的意思(A source file may start with a package declaration)一个代码源文件当中可以引入packages,当然了也可以不引入,那么这就引出了下面的default imports的概念了。Default Imports
A number of packages are imported into every Kotlin file by default:
- kotlin.*
- kotlin.annotation.*
- kotlin.collections.*
- kotlin.comparisons.* (since 1.1)
- kotlin.io.*
- kotlin.ranges.*
- kotlin.sequences.*
- kotlin.text.*
Additional packages are imported depending on the target platform:
- JVM:
- java.lang.*
- kotlin.jvm.*
- JS:
- kotlin.js.*
Imports
Apart from the default imports, each file may contain its own import directives.Syntax for imports is described in thegrammar.
We can import either a single name, e.g.
import foo.Bar // Bar is now accessible without qualification
or all the accessible contents of a scope (package, class, object etc):
import foo.* // everything in 'foo' becomes accessible
If there is a name *, we can disambiguate by usingas keyword to locally rename the *ing entity:
import foo.Bar // Bar is accessible
import bar.Bar as bBar // bBar stands for 'bar.Bar'
The import
keyword is not restricted to importing classes; you can also use it to import other declarations:
- top-level functions and properties;
- functions and properties declared in object declarations;
- enum constants
Unlike Java, Kotlin does not have a separate "import static" syntax; all of these declarations are imported using the regularimport
keyword.
编写代码的时候,除了使用系统的API之外,还有一大堆是自己张罗的代码文件包括了很多的类/函数等等东西,因此在不同的文件之间就是需要通过imports的方式将这些需要互相调用的类等等引进来了,那么就是”imports“。引入的方式可以是具体的指向特定的名字,也可以是引入一个”区域“的资源。
另外值得注意的是上面黄色背景部分,如果是在引入的包当中有冲突的类的时候,可以使用”as“关键字进行申明区别。
Visibility of Top-level Declarations
If a top-level declaration is marked private, it is private to the file it's declared in (seeVisibility Modifiers).
如果申明了private的话,那么在他所申明的文件里他就是属于私有的东西了。综上:package的这些相关语法和Java基本没差,只是其中有一个包冲突时候的“as”关键字的使用和Java不一样,Java里面好像没有,我没有使用过。
——————————————————————————————————————————————————————————————————————————
回到packages下面的函数申明和编写部分
二 定义函数
Defining functions
Function having twoInt
parameters with
Int
return type:
fun sum(a: Int, b: Int): Int {
return a + b
}
第一个函数示例是两个int类型的形参和int返回值的函数,这个官方文档做的很好,点击中间的“+”符号可以看到为这个函数准备的Java主函数,然后点击右侧的那个“播放”按钮就可以运行这个函数得出结果了:
可以看到函数的写法和Java的已经是大相径庭了,便于对比,我将上述的kotlin的函数敲成Java的函数如下:
public int sum(int a, int b){通过对比发现,函数的申明变成了“fun”,而前面也没有类似Java的“public”或者“private”等作用域的限制关键字了,形参里面改用了“:“冒号作为形参类型和形参变量之间的对应,函数申明一行的后面又通过”:“冒号跟了一个返回值的类型,注意是Int而不是int哦!第一个字母大写了,这里返回值为int类型,另外,每一行的后面都没有了”;“分号作为一行/句代码的结尾标识。
return a+b;
}
再看看主函数的写法:
fun main(args: Array<String>) {主函数的函数申明一行没有出现返回值的定义,那么就是说明,如果没有返回值的话,函数申明一行是不需要添加冒号+返回值类型这个的。主函数部分主要看看函数体里面的输出打印语句print和println两个函数,这两行看起来都是比较正常的,一个输出了字符串,接着第二行调用函数赋值然后输出赋值后的运算结果。但其中有一个肯定有换行功能的,不知道是不是和Java下的println是不是一样的,自己写一下测试了,println是带换行的那个,这里顺便丢一张kotlin在线编程的界面:
print("sum of 3 and 5 is ")
println(sum(3, 5))
}
接着看第二种函数申明格式:函数的申明和函数体都写在了一起,返回值可以推断。
Function with an expression body and inferred return type:
fun sum(a: Int, b: Int) = a + b这个函数没有申明返回值类型,主函数体的println里面出现了一个”$“符号调用上面的函数,并传值了。这个”$“的作用也很明显了,但是注意通过”$"调用函数的时候是通过“{~~}”括起来的。那么在继”:“冒号之后这是第二个新的符号出现了。通过测试发现这样的申明形式虽然没有返回值申明,但应该是默认了Int类型了,但是如果形参里面是两种不同类型的参与运算就会报错了:
fun main(args: Array<String>) {
println("sum of 19 and 23 is ${sum(19, 23)}")
}
Simplest version.kt
Error:(9, 16) The integer literal does not conform to the expected type Float
第三种函数格式:返回无意义的值
Function returning no meaningful value:
fun printSum(a: Int, b: Int): Unit {函数体里面的println语句通过“$”做了一个简单的运算。
println("sum of $a and $b is ${a + b}")
}
fun main(args: Array<String>) {
printSum(-1, 8)
}
第四种函数格式:返回值可忽略
Unit
return type can be omitted:
fun printSum(a: Int, b: Int) {和上一个一样,只是说明了Unit的返回值的话,可以将其忽略掉不写。
println("sum of $a and $b is ${a + b}")
}
fun main(args: Array<String>) {
printSum(-1, 8)
}
三,变量声明
Defining local variables
赋值一次的变量Assign-once (read-only) local variable:
fun main(args: Array<String>) {
val a: Int = 1 // immediate assignment
val b = 2 // `Int` type is inferred
val c: Int // Type required when no initializer is provided
c = 3 // deferred assignment
println("a = $a, b = $b, c = $c")
}
基本规则就是以“val”关键字开始,然后是变量名称,“:”,变量类型,赋值。这样子。但是可以在使用的时候省略变量类型,通过赋值来推断,也可以在申明的时候暂时不赋值,在需要的时候再赋值。
可变变量赋值
Mutable variable:
fun main(args: Array<String>) {
var x = 5 // `Int` type is inferred
x += 1
println("x = $x")
}
上面是省略了变量类型的。
三, 注释
Comments
Just like Java and JavaScript, Kotlin supports end-of-line and block comments.
// This is an end-of-line comment
/* This is a block comment
on multiple lines. */
Unlike Java, block comments in Kotlin can be nested.
注释和Java一样,就是两种形式:// 一行的注释,/**/ 多行注释。不过不同于Java的多行注释不能嵌套存在,kotlin的多行注释是可以嵌套的哦。
四,字符串相关定义和使用
Using string templates
fun main(args: Array<String>) {函数体里面有个replace方法,和Java一样的。
var a = 1
// simple name in template:
val s1 = "a is $a"
a = 2
// arbitrary expression in template:
val s2 = "${s1.replace("is", "was")}, but now is $a"
println(s2)
}
五,条件表达式的使用
Using conditional expressions
fun maxOf(a: Int, b: Int): Int {
if (a > b) {
return a
} else {
return b
}
}
和Java没差。
六,重要特性:可为空的值的使用和检查是否为空
Using nullable values and checking fornull
A reference must be explicitly marked as nullable when null value is possible.
特别注意,当一个值可为空的时候,必须明确标明其可为空!
Return null if str
does not hold an integer:
fun parseInt(str: String): Int? {
// ...
}
上述函数申明部分返回值为Int,但是后面加上了一个“?"问号,意思即是说如果解析不成功或其他情况Int返回值可能是空,但是并不会产生异常。
fun parseInt(str: String): Int? {
return str.toIntOrNull()
}
fun printProduct(arg1: String, arg2: String) {
val x = parseInt(arg1)
val y = parseInt(arg2)
// Using `x * y` yields error because they may hold nulls.
if (x != null && y != null) {
// x and y are automatically cast to non-nullable after null check
println(x * y)
}
else {
println("either '$arg1' or '$arg2' is not a number")
}
}
fun main(args: Array<String>) {
printProduct("6", "7")
printProduct("a", "7")
printProduct("a", "b")
}
or
fun parseInt(str: String): Int? {
return str.toIntOrNull()
}
fun printProduct(arg1: String, arg2: String) {
val x = parseInt(arg1)
val y = parseInt(arg2)
// ...
if (x == null) {
println("Wrong number format in arg1: '${arg1}'")
return
}
if (y == null) {
println("Wrong number format in arg2: '${arg2}'")
return
}
// x and y are automatically cast to non-nullable after null check
println(x * y)
}
fun main(args: Array<String>) {
printProduct("6", "7")
printProduct("a", "7")
printProduct("99", "b")
}
注意黄色背景部分的输出函数哦!在输入为字符,转换为Int失败后,这里是没有报错的哦!
这便是kotlin的”空安全“机制,详见:http://kotlinlang.org/docs/reference/null-safety.html
七,类型检查和自动转换
Using type checks and automatic casts
The is operator checks if an expression is an instance of a type.If an immutable local variable or property is checked for a specific type, there's no need to cast it explicitly:通过”is“关键字/运算符来检查表达式左边是否是右边类型的一个实例,如果是特定类型检查局部变量或者属性,则不需要显示的转换
fun getStringLength(obj: Any): Int? {
if (obj is String) {
// `obj` is automatically cast to `String` in this branch
return obj.length
}
// `obj` is still of type `Any` outside of the type-checked branch
return null
}
fun main(args: Array<String>) {
fun printLength(obj: Any) {
println("'$obj' string length is ${getStringLength(obj) ?: "... err, not a string"} ")
}
printLength("Incomprehensibilities")
printLength(1000)
printLength(listOf(Any()))
}
or
fun getStringLength(obj: Any): Int? {
if (obj !is String) return null
// `obj` is automatically cast to `String` in this branch
return obj.length
}
fun main(args: Array<String>) {
fun printLength(obj: Any) {
println("'$obj' string length is ${getStringLength(obj) ?: "... err, not a string"} ")
}
printLength("Incomprehensibilities")
printLength(1000)
printLength(listOf(Any()))
}
or even
fun getStringLength(obj: Any): Int? {
// `obj` is automatically cast to `String` on the right-hand side of `&&`
if (obj is String && obj.length > 0) {
return obj.length
}
return null
}
fun main(args: Array<String>) {
fun printLength(obj: Any) {
println("'$obj' string length is ${getStringLength(obj) ?: "... err, is empty or not a string at all"} ")
}
printLength("Incomprehensibilities")
printLength("")
printLength(1000)
}
上述类型转换和Java类似,另外,在代码当中出现了”?:“符号,查阅了其他网络资料后知道是空判断的表达式符号,相关介绍如下:
//类型后面加?表示可为空
var age: String? = "23"
//抛出空指针异常
val ages = age!!.toInt()
//不做处理返回 null
val ages1 = age?.toInt()
//age为空返回-1
val ages2 = age?.toInt() ?: -1
其中的”?“符号是可为空,”!!“是像Java一样抛出空异常,"?:"则是再进行判断”如果为空,则执行后面那句“。
八,for 循环
Using a for
loop
fun main(args: Array<String>) {
val items = listOf("apple", "banana", "kiwi")
for (item in items) {
println(item)
}
}
fun main(args: Array<String>) {
val items = listOf("apple", "banana", "kiwi")
for (index in items.indices) {
println("item at $index is ${items[index]}")
}
}
注意,使用的是”in“关键字,第一个fun直接输出的是listOf的元素,如果要输出带索引的元素,就使用第二种方式,还有另外一个方法”
withIndex
“:
for ((index, value) in array.withIndex()) {
println("the element at $index is $value")
}
九, while循环
Using a while
loop
fun main(args: Array<String>) {
val items = listOf("apple", "banana", "kiwi")
var index = 0
while (index < items.size) {
println("item at $index is ${items[index]}")
index++
}
}
十,when 表达式
Using when
expression
fun describe(obj: Any): String =
when (obj) {
1 -> "One"
"Hello" -> "Greeting"
is Long -> "Long"
!is String -> "Not a string"
else -> "Unknown"
}
fun main(args: Array<String>) {
println(describe(1))
println(describe("Hello"))
println(describe(1000L))
println(describe(2))
println(describe("other"))
}
看示例便知,新表达式when和Java的switch很相像,不过从表面上看,语法是不一样的,但是表达的意思我认为几乎一致,是case~~default,细节地方还有待使用时继续研究。
十一,ranges
Using ranges
fun main(args: Array<String>) {看上去是作为判断范围的,自己写了下,这个范围是个闭区间。注意其中的关键字”in“和”.."
val x = 10
val y = 9
if (x in 1..y+1) {
println("fits in range")
}
}
一个用于判断数字是否在某个范围的例子:
fun main(args: Array<String>) {上面最后一行代码打印出的结果值得注意一下: 3 and 0..2
val list = listOf("a", "b", "c")
if (-1 !in 0..list.lastIndex) {
println("-1 is out of range")
}
if (list.size !in list.indices) {
println("list size is out of valid list indices range too")
}
println("${list.size} and ${list.indices}")
}
这个新特性用于循环打印输出的例子:
fun main(args: Array<String>) {注意后面那个函数使用了“step”步长以及“downTo”向下输出,感觉好像很实用。
for (x in 1..5) {
print(x)
}
}
fun main(args: Array<String>) {
for (x in 1..10 step 2) {
print(x)
}
for (x in 9 downTo 0 step 3) {
print(x)
}
}
十二,集合的使用
Using collections
通过for迭代输出元素。Iterating over a collection:
fun main(args: Array<String>) {使用“in”检查集合当中是否含有这个object。Checking if a collection contains an object using in operator:
val items = listOf("apple", "banana", "kiwi")
for (item in items) {
println(item)
}
}
fun main(args: Array<String>) {
val items = setOf("apple", "banana", "kiwi")
when {
"orange" in items -> println("juicy")
"apple" in items -> println("apple is fine too")
}
}
使用lambda表达式过滤和映射集合:Using lambda expressions to filter and map collections:
fun main(args: Array<String>) {
val fruits = listOf("banana", "avocado", "apple", "kiwi")
fruits
.filter { it.startsWith("a") }
.sortedBy { it }
.map { it.toUpperCase() }
.forEach { println(it) }
}
lambda没用过~~
好了,以上就是这些基本语法,大致过了一遍,里面还有各个点的细节介绍还没看。有和Java类似的,但大部分都是新的东西。