Kotlin 是一種在 Java 虛擬機上執行的靜態型別編程語言,由 JetBrains 開發團隊所開發。該語言有幾個優勢。
1. 簡潔
它大大減少你需要寫的樣板代碼的數量。
2. 安全
避免空指針異常等整個類的錯誤。
3. 通用
構建服務器端程序、Android 應用程序或者在瀏覽器中運行的前端程序。
4. 互操作性
通過 100% Java 互操作性,利用 JVM 既有框架和庫。
在我們的AndroidStudio開發工具中,要想使用Kotlin這個優秀的開發語言,我們需要安裝插件,直接在安裝插件界面搜索Kotlin然後安裝。之後再gradle文件增加如下配置
1
2
3
4
5
6
|
apply
plugin
:
'kotlin-android'
apply
plugin
:
'kotlin-android-extensions'
dependencies
{
compile
"org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
}
|
項目gradle文件
1
2
3
4
5
6
7
8
9
10
11
12
13
|
buildscript
{
ext
.
kotlin_version
=
'1.1.1'
repositories
{
jcenter
(
)
}
dependencies
{
classpath
'com.android.tools.build:gradle:2.3.1'
classpath
"org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
|
完成上面的配置後,我們就可以愉快的玩耍了。
首先我們還和以前一樣,創建一個Android項目,自動創建一個Activity之後我們再創建一個java類
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
public
class
MainActivity
extends
AppCompatActivity
{
@
Override
protected
void
onCreate
(
Bundle
savedInstanceState
)
{
super
.
onCreate
(
savedInstanceState
)
;
setContentView
(
R
.
layout
.
activity_main
)
;
Toolbar
toolbar
=
(
Toolbar
)
findViewById
(
R
.
id
.
toolbar
)
;
setSupportActionBar
(
toolbar
)
;
FloatingActionButton
fab
=
(
FloatingActionButton
)
findViewById
(
R
.
id
.
fab
)
;
fab
.
setOnClickListener
(
new
View
.
OnClickListener
(
)
{
@
Override
public
void
onClick
(
View
view
)
{
Snackbar
.
make
(
view
,
"Replace with your own action"
,
Snackbar
.
LENGTH_LONG
)
.
setAction
(
"Action"
,
null
)
.
show
(
)
;
}
}
)
;
}
@
Override
public
boolean
onCreateOptionsMenu
(
Menu
menu
)
{
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater
(
)
.
inflate
(
R
.
menu
.
menu_main
,
menu
)
;
return
true
;
}
@
Override
public
boolean
onOptionsItemSelected
(
MenuItem
item
)
{
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int
id
=
item
.
getItemId
(
)
;
//noinspection SimplifiableIfStatement
if
(
id
==
R
.
id
.
action_settings
)
{
return
true
;
}
return
super
.
onOptionsItemSelected
(
item
)
;
}
}
public
class
Test
{
private
static
String
str
=
null
;
public
static
void
main
(
String
[
]
args
)
{
str
=
"Code4Android"
;
System
.
out
.
println
(
str
)
;
}
}
|
那上面的代碼如果用kotlin實現是什麼樣子呢。儘管現在我們還不能寫出Kotlin代碼,但是在安裝插件後AS中提供了自動轉換Kotlin代碼的功能
轉換後的Kotlin代碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
class
MainActivity
:
AppCompatActivity
(
)
{
override
fun
onCreate
(
savedInstanceState
:
Bundle
?
)
{
super
.
onCreate
(
savedInstanceState
)
setContentView
(
R
.
layout
.
activity_main
)
val
toolbar
=
findViewById
(
R
.
id
.
toolbar
)
as
Toolbar
setSupportActionBar
(
toolbar
)
val
fab
=
findViewById
(
R
.
id
.
fab
)
as
FloatingActionButton
fab
.
setOnClickListener
{
view
->
Snackbar
.
make
(
view
,
"Replace with your own action"
,
Snackbar
.
LENGTH_LONG
)
.
setAction
(
"Action"
,
null
)
.
show
(
)
}
}
override
fun
onCreateOptionsMenu
(
menu
:
Menu
)
:
Boolean
{
// Inflate the menu; this adds items to the action bar if it is present.
menuInflater
.
inflate
(
R
.
menu
.
menu_main
,
menu
)
return
true
}
override
fun
onOptionsItemSelected
(
item
:
MenuItem
)
:
Boolean
{
val
id
=
item
.
itemId
if
(
id
==
R
.
id
.
action_settings
)
{
return
true
}
return
super
.
onOptionsItemSelected
(
item
)
}
}
object
Test
{
private
var
str
:
String
?
=
null
@JvmStatic
fun
main
(
args
:
Array
<String>
)
{
str
=
"Code4Android"
println
(
str
)
}
}
|
注意:AS提供的java代碼自動轉換功能,我們不要輕易使用,更不要轉化我們成熟的項目,如果需要就需要我們自己去重構實現。否則會有意向不到的事情等着你,畢竟轉換不是那麼智能。上面的代碼只是讓你先簡單熟悉下Kotlin代碼時什麼樣子的,接下來我們先去學習一下Kotlin的基本語法。相信很容易上手。
我們由一個簡單的」Hello World!」輸出程序開始。與新建java文件類似,如下圖,我們選擇Kotlin File/Class.創建一個Kotlin文件。
1
2
3
4
5
6
7
8
9
10
11
12
|
package
com
.
learnrecord
/**
*Created by Code4Android on 2017/4/21.
*/
var
str
:
String
=
""
fun
main
(
args
:
Array
<String>
)
{
str
=
"Hello World!"
println
(
str
)
}
|
上述代碼就是簡單的輸出一個字符串「Hello World」,package 後面跟的是包名,我們看出了和java文件的區別,在包名後面沒有以分號「;」結尾。在Kotlin語法中,語句結尾都不再有分號「;」。
在Kotlin中變量聲明有兩種類型,val修飾變量是隻讀變量即只能賦值一次,再次賦值時就會編譯錯誤,如果我們需要多次修改值就需要使用var。在上面的 var str: String = 「」中,str是變量名,:String 表明該變量是String類型變量,後面就是賦值語句。我們也可以這樣寫var str= 「」省略了生命變量類型,它可以根據賦的值而自動推斷出類型。如果我們使用下面賦值語句str=null,發現null是不能賦值的,這就是Kotlin的特性,如果我們想賦值null,可以修改爲 var str:String?=」「。
fun就是函數生命,而這個main函數就和我們java中的main方法一樣,是程序執行的入口。println就是一個打印輸出。
在Kotlin中有如下幾種Number類型,他們都是繼承自Number抽象類。
Float(32位),Double(64),Int(32),Byte(8),Short(16),Long(64,類型用大寫L,如12L),Any(任意類型),數組類型Array 根據傳入的泛型數據自動匹配類型,Kotlin還提供了指定類型的Array,如ByteArray,CharArray,ShortArray,IntArray,LongArray,FloatArray,DoubleArray,BooleanArray。在數組類型中都提供了get(index),set(index,value)及iterator()方法供我們使用。
1
2
3
4
|
val
iArray
:
IntArray
=
intArrayOf
(
1
,
2
,
3
)
val
sArray
:
Array
<
String
>
=
Array
<
String
>
(
3
,
{
i
->
i
.
toString
(
)
}
)
val
anyArray
:
Array
<
Any
>
=
arrayOf
(
1
,
"2"
,
3.0
,
4.1f
)
// 可將類型進行混排放入同一個數組中
val
lArray
:
LongArray
=
longArrayOf
(
1L
,
2L
,
3L
)
|
我們先來實現一個簡單的數值求和的函數,通用實現方法如下
1
2
3
|
fun
sum
(
a
:
Int
,
b
:
Int
)
:
Int
{
return
a
+
b
}
|
傳入兩個Int型數值,sum是函數名,括號後面的:Int表示該函數返回Int的值,函數體中對兩個數字相加並返回。在Kotlin中表達式也可以作爲函數體,編譯器可以推斷出返回類型,可以簡化爲
1
|
fun
sum
(
a
:
Int
,
b
:
Int
)
=
a
+
b
|
爲了更好理解表達式可以作爲函數體,我們可以創建一個函數獲取兩個數的最大值,如下:
1
|
fun
max1
(
a
:
Int
,
b
:
Int
)
=
if
(
a
>
b
)
a
else
b
|
需要注意的是若if後有多個表達式,如下
1
2
3
4
5
6
7
8
9
|
fun
max1
(
a
:
Int
,
b
:
Int
)
=
if
(
a
>
b
)
{
println
(
a
)
a
}
else
{
println
(
b
)
//如果我們將println(b)放到b的下面,運行會返回kotlin.Unit爲無類型,返回值總是最後一個表達式的返回值或值
b
}
println
(
max1
(
1
,
3
)
)
|
括號中的表達式順序決定了返回的值及其類型。
如果我們的方法體僅僅是打印字符串,並不返回值則
1
2
3
|
fun
printInt
(
a
:
Int
)
:
Unit
{
println
(
a
)
}
|
Unit就類似我們java中的void,即沒有返回值,此時我們可以省略
1
2
3
|
fun
printInt
(
a
:
Int
)
{
println
(
a
)
}
|
對於函數體,方法或者類等和java一樣也有一些修飾符,如下
直接上代碼如下
1
2
3
4
5
6
7
8
9
|
//操作符 shl 下面對Int和Long
var
a
:
Int
=
4
var
shl
:
Int
=
a
shl
(
1
)
//Java中的左移運算符 <<
var
shr
:
Int
=
a
shr
(
1
)
//Java中的右移運算符 >>
var
ushr
:
Int
=
a
ushr
(
3
)
//無符號右移,高位補0 >>>
var
and
:
Int
=
2
and
(
4
)
//按位與操作 &
var
or
:
Int
=
2
or
(
4
)
//按位或操作 |
var
xor
:
Int
=
2
xor
(
6
)
//按位異或操作 ^
print
(
"\nshl:"
+
shl
+
"\nshr:"
+
shr
+
" \nushr:"
+
ushr
+
"\nand:"
+
and
+
"\nor:"
+
or
+
"\nxor:"
+
xor
)
|
輸出信息爲
1
2
3
4
5
6
|
shl
:
8
shr
:
2
ushr
:
0
and:
0
or
:
6
xor
:
4
|
在上面的部分操作符可達到邏輯操作符, 當我們使用Boolean時,or() 相當於 ||,and() 相當於 &&, xor() 當操作符兩邊相反時爲true,否則爲false ,not()時取反。
遍歷數組
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
fun
forLoop
(
array
:
Array
<String>
)
{
//第一種方式直接輸出字符(類似java foreach)
for
(
str
in
array
)
{
println
(
str
)
}
//Array提供了forEach函數
array
.
forEach
{
println
(
it
)
}
//array.indices是數組索引
for
(
i
in
array
.
indices
)
{
println
(
array
[
i
]
)
}
//(類似javafor(int i=0;i<arry.length;i++))
var
i
=
0
while
(
i
<
array
.
size
)
{
println
(
array
[
i
++
]
)
}
}
|
使用when判斷類型
1
2
3
4
5
6
7
8
9
|
fun
whenFun
(
obj
:
Any
)
{
when
(
obj
)
{
25
->
println
(
"25"
)
"Kotlin"
->
println
(
"Kotlin"
)
!
is
String
->
println
(
"Not String"
)
is
Long
->
println
(
"Number is Long"
)
else
->
println
(
"Nothing"
)
}
}
|
is 和java中instanceof是一個作用判斷是否爲某個類型。!is即判斷不是某個類型。
1
2
3
4
5
6
7
8
9
|
//@定義label,一般用在內層循環中跳到外層循環:i in 0..2等價於java中 for(int i=0;i<=2;i++)效果
loop
@
for
(
i
in
0..2
)
{
for
(
j
in
0..3
)
{
println
(
"i:"
+
i
+
" j:"
+
j
)
if
(
j
==
2
)
//[email protected]//跳到外層循環,繼續往下執行
break
<
a
href
=
"http://www.jobbole.com/members/loop"
>
@loop
<
/
a
>
//跳到外層循環label處,跳出改層循環
}
}
|
倒序輸出是downTo
1
2
3
4
5
6
7
8
|
//倒序輸出5 4 3 2 1 0
for
(
i
in
5
downTo
0
)
{
println
(
i
)
}
//設置輸出數據步長
for
(
i
in
1..5
step
3
)
print
(
i
)
// 輸出 14
//step和downTo混合使用
for
(
i
in
5
downTo
1
step
3
)
print
(
i
)
//輸出52
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
/*** constructor:構造函數
* constructor無修飾符(如:private)時,constructor可以省略:
* 當是無參構造函數時,整個構造函數部分也可以省略,省略的構造函數默認是public的
* 由於primary constructor不能包含任何代碼,因此使用 init 代碼塊對其初始化,
* 同時可以在初始化代碼塊中使用構造函數的參數
*/
open
class
People
private
constructor
(
var
id
:
String
,
var
name
:
String
)
{
//可以類中初始化屬性:
var
customName
=
name
.
toUpperCase
(
)
//初始化屬性
//次構造函數,使用constructor前綴聲明,且必須調用primary constructor,使用this關鍵字
constructor
(
id
:
String
,
name
:
String
,
age
:
Int
)
:
this
(
id
,
name
)
{
println
(
"constructor"
)
}
init
{
println
(
"初始化操作,可使用constructor參數"
)
}
//需要open修飾,子類纔可以
open
fun
study
(
)
{
print
(
"study"
)
}
//People前的冒號":"是繼承的意思,實現類接口的時候也是冒號
class
Student
(
id
:
String
,
name
:
String
)
:
People
(
id
,
name
)
{
var
test
:
Number
=
3
private
var
name1
:
String
?
get
(
)
{
return
name1
}
set
(
value
)
{
name1
=
value
}
//override修飾的方法,默認是可以被繼承的。若希望不被繼承,可以使用 final 關鍵詞修飾
override
fun
study
(
)
{
super
.
study
(
)
}
}
}
|
數據類用來保存數據,類似於POJO類,使用data關鍵詞進行定義,編譯器默認會爲數據類生成以下四個方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
public
class
StaffJ
<T>
{
private
String
name
;
private
String
position
;
private
T
age
;
public
String
getName
(
)
{
return
name
;
}
public
void
setName
(
String
name
)
{
this
.
name
=
name
;
}
public
String
getPosition
(
)
{
return
position
;
}
public
void
setPosition
(
String
position
)
{
this
.
position
=
position
;
}
public
T
getAge
(
)
{
return
age
;
}
public
void
setAge
(
T
age
)
{
this
.
age
=
age
;
}
}
|
Kotlin數據類:
1
|
data
class
Staff
<
T
>
(
var
name
:
String
,
val
position
:
String
,
var
age
:
T
)
|
通過對比我們就看出了優點了,一行代碼就實現了,具體使用
1
|
var
staff
=
Staff
(
"code4Android"
,
"Android工程師"
,
"22"
)
//實例化對象
|
要獲取某個屬性如獲取名字staff.name,賦值就是staff.name=」code4Android2」,既然說了這樣可以賦值,但是動手的小夥伴說爲什麼我敲的報錯啊,如下
1
|
staff
.
position
=
"前端"
|
編譯報錯了,在前面我們說過val修飾的屬性只能賦值一次,那在這裏val修飾的屬性我們是不能再次賦值的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
fun
main
(
arg
:
Array
<String>
)
{
var
staff
=
Staff
(
"code4Android"
,
"Android工程師"
,
"22"
)
//實例化對象
staff
.
name
=
"code4Android2"
var
staff1
=
staff
.
copy
(
)
//使用copy的時候可以指定默認值,可以指定任意個用逗號","隔開
var
staff2
=
staff
.
copy
(
name
=
"ccc"
,
position
=
"kotlin"
)
println
(
"name:${staff2.name} position:${staff2.position} age ${staff2.age}"
)
//staff.position="Kotiln" //val不能再次賦值
var
anotherStaff
=
Staff
(
"code4Android"
,
"Android工程師"
,
22
)
//實例化對象
println
(
"staff toString(): ${staff.toString()} anotherStaff toString(): ${anotherStaff.toString()}"
)
println
(
"staff hashCode(): ${staff.hashCode()} anotherStaff hashCode(): ${anotherStaff.hashCode()}"
)
println
(
"staff is equals another staff ? ${staff.equals(anotherStaff)}"
)
}
|
上面使用了字符模板,在Kotlin中有兩種字符模板形式,$<變量>、${<變量>}
1
2
3
|
var
name
:
String
=
"Code4Android"
println
(
"我的名字叫$name"
)
println
(
"我的名字叫${name}"
)
|
1
2
3
4
5
6
7
8
9
10
11
|
/**
* java允許使用匿名內部類;kotlin也有類似的概念,稱爲對象表達式-object expressions
*/
open
class
KeyBord
{
open
fun
onKeyEvent
(
code
:
Int
)
:
Unit
=
Unit
}
/**匿名內部類**/
var
key
=
object
:
KeyBord
(
)
{
override
open
fun
onKeyEvent
(
code
:
Int
)
:
Unit
=
Unit
}
|
枚舉
1
2
3
4
5
6
7
8
9
10
|
enum
class
Color
{
RED
,
BLACK
,
BLUE
,
GREEN
,
WHITE
}
fun
display
(
)
{
var
color
:
Color
=
Color
.
BLACK
Color
.
valueOf
(
"BLACK"
)
// 轉換指定name爲枚舉值,若未匹配成功,會拋出IllegalArgumentException
Color
.
values
(
)
//已數組的形式,返回枚舉值
println
(
color
.
name
)
////獲取枚舉名稱
println
(
color
.
ordinal
)
//獲取枚舉值在所有枚舉數組中定義的順序,0開始
}
|
在Kotlin中枚舉還支持方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
/**
* fun receiverType.functionName(params){
*body
*}
* receiverType : 待擴展的類名
* . :修飾符爲擴展符
* functionName :爲自定義的擴展函數名,
* params :爲自定義的擴展函數參數,可爲空
* 擴展函數作用域,受函數的visibility modifiers影響
* 擴展函數並沒有對原類做修改,而是爲被擴展類的對象添加新的函數。
* 有一條規則,若擴展函數和類原有函數一致,則使用該函數時,會優先使用類本身的函數。
*/
class
Employee
(
var
name
:
String
)
{
fun
print
(
)
{
println
(
"Employee"
)
}
}
//擴展函數
fun
Employee
.
println
(
)
{
print
(
"println:Employee name is $name"
)
}
/**
* 可以擴展一個空對象
*/
fun
Any
?
.
toString1
(
)
:
String
{
if
(
this
==
null
)
return
"toString1:null"
else
{
return
"toString1"
+
toString
(
)
}
}
/**
* 擴展屬性
* 由於擴展屬性實際上不會向類添加新的成員,
* 因此無法讓一個擴展屬性擁有一個後端域變量. 所以,對於擴展屬性不允許存在初始化器.
* 擴展屬性的行爲只能通過明確給定的取值方法與設值方法來定義,也就意味着擴展屬性只
* 能被聲明爲val而不能被聲明爲var.如果強制聲明爲var,即使進行了初始化,
* 在運行也會報異常錯誤,提示該屬性沒有後端域變量。
/**
* 擴展屬性
* 由於擴展屬性實際上不會向類添加新的成員,
* 因此無法讓一個擴展屬性擁有一個後端域變量. 所以,對於擴展屬性不允許存在初始化器.
* 擴展屬性的行爲只能通過明確給定的取值方法與設值方法來定義,也就意味着擴展屬性只
* 能被聲明爲val而不能被聲明爲var.如果強制聲明爲var,即使進行了初始化,
* 在運行也會報異常錯誤,提示該屬性沒有後端域變量。
*/
val
Employee
.
lastName
:
String
get
(
)
{
return
"get:"
+
name
{
return
"get:"
+
name
}
|
使用
1
2
3
|