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
|