Kotlin
第一次知道kotlin这个语言是在JakeWharton的这个dex-method-list 项目里,本来这个项目用的是java后来大神又用Kotlin实现了一下,前两天1.0正式版发布了,被各种新闻刷了一下后有必要学习一下。
花了半天看完了《Kotlin for Android》这份文档,之前很多人说像Swift,看完这份文档后才发现它更像C#,比如带问号的可null类型,委托,lambda和的Linq类似,还有 explicit,implicit cast (协变逆变),sealed(密封类),里面还提供了和Linq To Sql 很像的Anko框架,以及getter,setter。
一. Android下的Kotlin
按照惯例我们做一个"Helloworld"项目来尝尝鲜
环境配置
使用Kotlin开发Android应用需要安装Android Studio 和其配套的插件 需要安装Kotlin插件,是使Android Studio支持Kotlin的基础插件,在《Kotlin for Android》一书中说还需要 Kotlin Extensions For Android 插件,这个插件在最近的版本中已经obsolote,这个插件已经被集成到Kotlin插件里,所以不需要安装这个插件了。
1. 使用AndroidStudio创建一个新项目
我是用的Android Studio是最新的2.0 Beta5,Gradle是2.10
2. 将MainActivity类转换成Kotlin代码
这是一个很给力的特性,可以把Java代码无缝的转换成Kotlin代码。这种转换并不是最优化的,但是在学习Kotlin的时候这个方法很有用,可以对比之前java代码和Kotlin代码语法的转换。 先打开MainActivity类,然后有三种方式进行转换:
- 选择
code
->Convert File To Kotlin File
- 选择
Help
->Find Action
或者使用快捷键 在弹出的对话框里输入convert
就会有提示,如下图 - 直接使用快捷键
shift+alt+command
可以直接触发转换工具
3. 配置Gradle
代码转换完成后会提示配置Kotlin,可以手动配置也可以使用Android Studio自带的配置工具进行配置。
暂不介绍手动配置,自动配置会在第一次代码转换完成后有个configure
提示(如下图),选择ok即可自动更新Gradle配置文件,非常方便
配置完成后Gradle配置文件里会自动加上kotlin gradle的插件配置
之后选择sync项目,将gradle插件下载下来即可运行项目了。
4. 运行项目
Kotlin里有个对Android开发来说很好用的特性就是它剔除了findViewById()方法的调用。
配置gralde
使用这个特性需要在app的build.gradle文件配置里增加下面这个plugin引用。
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
//增加这句
apply plugin: 'kotlin-android-extensions'
修改代码
将activity_main.xml
修改如下
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="dodola.kotlinandroid1.MainActivity">
<TextView
android:id="@+id/helloText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />
</RelativeLayout>
修改MainActivity.kt代码如下:
package dodola.kotlinandroid1
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
helloText.text="hello dodola"
}
}
我们需要import进一个特殊的包,import kotlinx.android.synthetic.main.activity_main.*
这个包有个命名规则,后面再介绍。
从helloText.text="hello dodola"
代码中可以看出不需要调用findViewById,也没有调用setText方法,而是直接使用text来进行赋值,这个使用的是Kotlin的一个叫做Getter Setter
技术,这种机制类似C#里的Accessors(访问器)
,后面再详细介绍,会发现很多特性都可以在C#里找到相应的技术,我之前是做.Net开发的,所以对C#相对敏感,后面会拿C#和Swift和Kotlin做对比,这样也有助于理解Kotlin里的一些技术。
二. Kotlin 介绍
下面介绍一下Kotlin语言的一些细节。内容是从官方文档和其他文章里总结得来的,目标是可以快速的学习Kotlin语言。
1. 基础
1.1 基本类型
Kotlin中没有像Java中的原始基本类型,基本类型都被当做对象形式存在。
数值类型
Kotlin提供的数值类型如下:
Type | Bitwidth |
---|---|
Double | 64 |
Float | 32 |
Long | 64 |
Int | 32 |
Short | 16 |
Byte | 8 |
需注意Char在Kotlin中不属于数值类型
字面值常量
整型:123
长整型:123L //需要增加L后缀
16进制:0x0f
二进制:0b00001011
Double:123.5,123.5e10//默认是Double类型
Float:123.5f //Float类型需要增加F或f后缀
注:不支持8进制字面值常量
val i = 12 // An Int
val iHex = 0x0f // 一个十六进制的Int类型
val l = 3L // A Long
val d = 3.5 // A Double
val f = 3.5F // A Float
显式转换
数值类型不会自动转型,必须要显示的进行类型转换,例子
//Byte -> Int
val b:Byte =1
val i:Int=b.toInt()
比如下面这种情况:
fun pow(a:Int):Double{
return Math.pow(a.toDouble(),2);//此处会出现Kotlin: The integer literal does not conform to the expected type kotlin.Double 的编译错误
}
上面例子pow(double a, double b)
传入的是两个Double类型,但是直接写入Int类型并不会进行隐式转换。需要做一次显示转换
fun pow(a:Int):Double{
return Math.pow(a.toDouble(),2.toDouble());
}
每个数值类型都支持下面转换:
toByte(): Byte
toShort(): Short
toInt(): Int
toLong(): Long
toFloat(): Float
toDouble(): Double
toChar(): Char
隐式转换,基于运算符重载技术,例子:
val l=1.toLong() + 1 //Long+Int=>Long
字符类型Char
字符类型使用Char表示,不能当成数值使用。
和Java一样,字符类型值是用单引号包起来的1
,'\n',可以显示转换成Int
类型
val c:Char='c'
val i: Int = c.toInt()
布尔值
val booltest=true
字符串
使用String
声明,Kotlin中有两种样式的String
一种是和Java类似的声明:
val s="Hello world"
一种是多行形式的表示
val text = """
for (c in "foo")
print(c)
"""
运行结果如下图: ![enter description here][9]
可以像数组那样访问,并且可以被迭代:
val s = "Example"
val c = s[2] // 这是一个字符'a'
// 迭代String
val s = "Example"
for(c in s){
print(c)
}
模板
字符串可以包含模板表达式,模板表达式由一个 $ 开始并包含另一个简单的名称:
val i = 10
val s = "i = $i"
//结果为 i = 10
或者是一个带大括号的表达式:+
val s = "abc"
val str = "$s.length is ${s.length}"
//结果为 abc.length is 3
1.2 数组
创建数组
指定长度
val sizeArray=arrayOfNulls<Int>(5)//sizeArray: Integer[5]{null}
val sizeArray1=IntArray(5)//sizeArray1: {0,0,0,0,0}
//同样的类型还有:BooleanArray,ByteArray,IntArray,CharArray,DoubleArray,FloatArray,LongArray,ShortArray,
使用值创建
//使用装箱操作
val sizeArray2=arrayOf(1,2,3,4,5)
/**
sizeArray2: Integer[]
0 = {Integer@447} "1"
1 = {Integer@448} "2"
2 = {Integer@449} "3"
3 = {Integer@450} "4"
4 = {Integer@451} "5"
**/
//下面方法创建的数组是未装箱的
val sizeArray3=intArrayOf(1,2,3,4,5)//sizeArray3:int[5] {1,2,3,4,5}
//同类的方法还有
//booeanArrayOf,byteArrayOf,doubleArrayOf,floatArrayOf,intArrayOf,longArrayOf,shortArrayOf
空数组
val emptyArray=emptyArray<Int>()//emptyArray: Integer[0]
访问数组
val arr = arrayOf(1, 2, 3)
println(asc[1]) // 1
println(asc.get(1)) // 1
遍历数组
for(i in arr){
println(i)
}
//1 2 3
for(j in asc.indices){
println(j)
}
//0 1 2
1.3 基本语法
定义包名
package my.demo //在源文件的最开头
import java.util.
包名不必和文件夹路径一致
定义类
class MainActivity{
//包含一个默认的构造器
}
构造方法
class Datas{
}
定义方法
//第一种形态
fun pow(a:Int):Double{
return Math.pow(a.toDouble(),2.toDouble());
}
//第二种形态,一个表达式函数体和一个可推断类型
fun pow(a:Int)= Math.pow(a.toDouble(),2.toDouble());
定义局部变量
用val
声明只读变量:
//-------in kotlin---------
val a:Int=1
val b = 1
val c:Int //声明
c=1
c=2//ERROR: val 只能赋值一次,这里会造成编译错误
//--------in java---------
final int a=1;
使用var
声明可变变量:
//--------in kotlin-------
var x=5
x+=1
//--------in java ---------
int x=5;
字符串模板
Kotlin允许在字符串中嵌入变量和表达式:
val name = "Bob"
println("My name is ${name}") //打印"My name is Bob"
val a = 10
val b = 20
println("The sum is ${a+b}") //打印"The sum is 30"
if 表达式
fun max(a: Int, b: Int): Int {
if (a > b)
return a
else
return b
}
fun max(a: Int, b: Int) = if (a > b) a else b
when表达式
用于替代switch
,但功能更强大
val age = 17
val typeOfPerson = when(age){
0 -> "New born"
in 1..12 -> "Child"
in 13..19 -> "Teenager"
else -> "Adult"
}
fun cases(obj: Any) {
when (obj) {
1 -> print("one")
"hello" -> print("Greeting")
is Long -> print("Long")
!is Long -> print("Not a string")
else -> print("Unknown")
}
}
注:else是必须的,相当于switch里的default
for 循环
fun sum(a: IntArray): Int {
var sumValue=0;
for(i in a){
sumValue+=i
}
return sumValue
}
//或者
fun sum(a: IntArray): Int {
var sumValue=0;
for(i in a.indices){
sumValue+=a[i]
}
return sumValue
}
while do ..while 循环
while和do..while循环的语法与Java完全相同。
fun sum(a: IntArray): Int {
var sumValue=0;
var i=0
while(i<a.size){
sumValue+=a[i++]
}
return sumValue
}
范围表达式
在Kotlin中,范围创建只需要..
操作符,例如:
val r1 = 1..5
//该范围包含数值1,2,3,4,5
如果创建一个降序范围,则需要使用downTo函数
val r2 = 5 downTo 1
//该范围包含数值5,4,3,2,1
默认范围的步长是1,可使用step方法修改步长:
val r3 = 5 downTo 1 step 2
//该范围包含数值5,3,1
可使用in
操作符检查数值是否在某个范围内
if (x in 1..y-1)
print("OK")
范围外:
if (x !in 0..array.lastIndex)
print("Out")
Lambda
val sumLambda: (Int, Int) -> Int = {x,y -> x+y}
1.4 包
声明包
package foo.bar
如果没有指定包名,那这个文件的内容就从属于没有名字的 "default" 包。
import
import foo.Bar //Bar 现在可以不用条件就可以使用
//或者范围内的所有可用的内容 (包,类,对象,等等):
import foo.*/ /foo 中的所有都可以使用
//如果命名有冲突,我们可以使用 as 关键字局部重命名解决冲突
import foo.Bar // Bar 可以使用
import bar.Bar as bBar // bBar 代表 'bar.Bar'
1.5 控制流
if表达式
//传统用法
var max = a
if (a < b)
max = b
//带 else
var max: Int
if (a > b)
max = a
else
max = b
//作为表达式
val max = if (a > b) a else b
//if分之可以作为块,最后一个表达式是该块的值
val max = if (a > b){
print("Choose a")
a
}
else{
print("Choose b")
b
}
when 表达式
一般用法,用来替代 switch,需注意的是里面的条件可以重复,如果条件重复的话则按照最先匹配的条件处理
val x=2
when(x){
1->print("1")
2->print("2")
else->print("else")//文档里写else 是mandatory的,但是去掉也可以
}
如果多个条件的处理方式一样,可以写作下面方式:
when (x) {
0,1 -> print("x == 0 or x == 1")
else -> print("otherwise")
}
可以用任意表达式作为分支的条件
when (x) {
parseInt(s) -> print("s encode x")
else -> print("s does not encode x")
}
可以用 in 或者 !in 检查值是否值在一个集合中:
when (x) {
in 1..10 -> print("x is in the range")
in validNumbers -> print("x is valid")
!in 10..20 -> print("x is outside the range")
else -> print("none of the above")
}
可以使用 is 判断是否是某个类型:
//官方的这个例子写的很奇怪,感觉为了使用 when 来故意这么写的
val hasPrefix = when (x) {
is String -> x.startsWith("prefix")
else -> false
}
//上面的例子可以直接写成
val hasPrefix=x.startsWith("prefix")
//下面的例子可能更好一些
var a=B()
when(a){
is C->print("A")
is B->print("B")
is A->print("C")
}
open class A{
}
open class B:A(){
}
class C:B(){
}
when 也可以用来代替 if-else, 如果没有提供任何参数则分支的条件就是表达式
when {
x.isOdd() -> print("x is odd")
x.isEven() -> print("x is even")
else -> print("x is funny")
}
测试例子:
fun main(args: Array<String>) {
var x=3
when(x){
1->println("1->1")
2->{
x=3
println("2->${x}")
}
3,4,5->println("3,4,5->")
test()->println("test()->${test()}")
in 7..10->println("6..10->${x}")
}
var a=B()
when(a){
is C->print("A")
is B->print("B")
is A->print("C")
}
}
open class A{
}
open class B:A(){
}
class C:B(){
}
fun test():Int{
println("=====test====")
return 6
}
for
for (item in collection)
print(item)
for (i in array.indices)
print(array[i])
while
```
## 返回与跳转
##标签
标签相当于 C语言 中的 label,通过@结尾来表示,比如 `abc@`
```kotlin
loop@ for(i in 1..100){
//
}
声明了标签之后可以使用 break 或者 continue 进行跳转: