google
宣布Kotlin
作为andorid
一级开发语言有一段时间了。在这段时间,我也在新的模块上尝试使用了kotlin
进行开发,经过这一段时间的开发,我觉得在开发中使用kotlin
是个很棒的选择。
使用Kotlin
的很容易,只需要进行几步简单的设置
-
android studio
安装下面两个插件,其中Parcelable Code Generator
不是必要的,主要用于序列化Parcelable
,所示最好也安装一下
image.png - 在
build.grade
文件中添加依赖compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
- 在
build.grade
文件中应用插件
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
通过以上的几步,就可以开始我们的kotlin
之旅了
1. 变量
在kotlin
中变量使用var、val
来表示,var
表示可变变量,val
表示不可变变量(相当于java
当中的final
)。如下所示
var name: String="ivy"
val age=12
kotlin
定义变量时可以不指定变量类型,kotlin
会根据你的赋值进行判断该变量的类型,如例子所示会自动把age
设置为Int
类型,这里需要注意的是如果需要把变量赋值为Null
(var name:String?="ivy"
),需要加上?
号才表示该变量可以为Null
2. 函数
kotlin
方法的使用和java
的类似,当没有返回值的时候可以不写,或者写Unit
fun add(one: Int , two: Int) : Int{
return one+two
}
同时,函数还可以为每个参数设置默认值,这样就可以避免多参数时定义多个方法,如下所示。
fun add(one:Int , two: Int ,three: Int=0){
//当参时没有设置three的值时,three默认为0
}
这里有一个比较重要的概念。在kotlin
当中函数是一级公民,即函数也是可以作为变量、返回值来使用的,如下所示
val add=fun (a: Int,b: Int){ println(a+b) }
add(1,2)
可能有的小伙伴就要说,这有什么卵用,我直接写个方法,然后调用方法不就好了。是,如果只是简单的调用是可以,那如果把函数作为方法的参数,这在java
就无法做到了,而把函数作为参数能给我们带来很大的方便,代码看起来也更有逻辑性。如下所示
fun judgeHasMoney(doHappyThing: ()->Unit){
if (User.hasMoney()){
println("no money")
}else{
doHappyThing()
}
}
judgeHasMoney(BuyCompany())
judgeHasMoney(BuyPhone())
这就可以非常方便对能否能购买东西进行判断,代码少了,看上去也更有逻辑性。还有很多函数作为一级公民好处的实例,这里就不一一演示了,for
循环、List
、Map
之类的太量操作符也是利用了这个特性进行实现。
3. 类的定义
class Boy(var name: String) : Person, interface{
init {
}
constructor(name:String,age: Int) : this(name) {
}
}
如例子所示,这就是kotlin
中类的定义。无论是接口还是父类都是写在冒号后面,用逗号分割,不再使用extends
和implement
。在kotlin
中,分主构造函数和次构造函数,主构造函数就是类名Boy
括号后面参数(如果想在类中直接使用构造函数的参数,需要加上val
或者var
),次构造函数必须以constructor
开头,并且必须继承调用主构造函数,所有的构造函数初始化后都会调用 Init{}
方法,所以可以在Init()
当中进行初始化操作。
在kotlin
类中还有一些需要注意的:
- 1 在
Kotlin
中,所有的方法都是不开放的(即子类无法重写),如果子类要重写父类的方法,需要在父类方法前面加上open
- 2 如果继承了两个类中含有共同的方法,可以通过
super<类名>.方法名
进行调用
class SuperMan(): Person(), Boy {
override fun run(){
super<Person>.run()
}
}
- 3 生成一个对象很简单,直接
var girl=Girl()
就可以,不再需要new
一个妹子了
4. 空安全
在android
开发中,空指针异常是最让我们头痛的也是导致应用崩溃率最高的问题。我们之前已经提到过,在kotlin
中,在定义变量的时候就需要指定变量是否允许为空,这在一定程度上帮助我们减少空指针异常的可能性。但是还是不能完成避免空指针的出现,因为有些变量就是可空可不为空,当我们调用时,就有可能因为没有做出判断而出现空指针异常。kotlin
也考虑到这个问题,所以在调用可为空的变量时,kotlin
会要求我们加上?
号,如下所示:
var school?=Class()
school?.grade?.class?student?.size()
如果我们想查找一个班的学生的人数,可以使用以上的方法,就可以获取到学生的人数,当中的任何一个变量为空,后面的都不会继续执行,这个链式调用就会返回null
,避免任何一个阶段为空出现NPE
的问题。
5. List、Map
的使用
在开发中,List
和Map
是我们最经常用到的类了。在kotlin
中,List
和Map
也分为可变和不可变
var a = listOf(4)
var b = mutableListOf<String>()
var c = mapOf(Pair("3","4"))
var d = mutableMapOf<String,String>()
不可变的概念即和数组类似,只能够改变每个位置的值,不能减少或者增加size
遍历List
有三种比较常用的方法
for(position in list.indices){
//每一个位置遍历
}
for(str in list){
//每一个对象遍历
}
for((position,str) in list.withIndex()){
//每一个位置和对象遍历
}
遍历Map
比较常用的方法
for((key,value) in map){
}
kotlin
的遍历方法相对于java
来说,还是很方便和实用的,但是!!!这不是最好用的!!最方便的是kotlin
加入了一系列的操作符,使用Rx的小伙伴应该知道操作符有多棒。下面我就来演示一个例子,查找一个班里面年龄超过15岁的女生名字
java
:看我的
for(student in students){
if(student.getSex()=="girl"&&student.getAge>15){
System.out.println(student.getName())
}
}
kotlin
:小样,看我的
students.filter{ it.sex=="girl" }
.filter{ it.age>15 }
.forEach{ print(student.name") }
是不是整个逻辑一目了然。但是,有些喜欢挑事的小伙伴可能就会说我觉得上面那个没有很方便啊。我就喜欢上面那个。
好,那我就再给你举两个例子,你写java
代码对比一下
- 1 15岁以上的女生是否有单身的!!!
students.filter { it.sex=="girl" }
.filter { it.age >15 }
.any{ !it.hasBoyFriend }
- 2 把15岁以上的女生年龄排序一下,打印出名字
students.filter { it.sex=="girl" }
.filter { it.age >15 }
.sortedBy { it.age }
.forEach { print(it.name) }
还有其它更棒,更好用的操作符,大家可以查看源码。操作符可以大大提高开发速度,我在这里就不一一展示了(Map
一样有各种好用的操作符)
6. 流程控制
- 1
for
语句的使用和java
的类似,也包含了很多的操作符(和List
类似)
for(i in 0 .. 100){
//0到100
}
for(i in 0 until 100){
//0到99
}
for (i in 100 downTo 0){
//100到0
}
- 2 在
java
中,when
语句中的case
判断只能是int
,后来也支持了String
,但是支持的类型还是很少。而且,只支持相同的类型,这样就导致我们在开发中遇到复杂业务的时候,往往还需要在case
中编写if
语句,这就给我带来了很大的麻烦,而且代码也比较混乱。但是kotlin
的when
语句很好的解决了这个问题。当when(x)
有参数时,可以在参数中添加参数所属类型的表达式。当when
不带参数时,可以添加各种表达式和方法,而不只是常量。
when (x) {
1 -> print("x == 1")
2,3 -> print("x == 2 or 3")
in 10..20 -> print("x is in the range")
else -> { // 注意这个块
print("x is shen me gui")
}
}
var x:Int=0
when {
x==1 -> print("x == 1")
x in 10..20 -> print("x is in the range")
isBigNum(x) -> print("big")
else -> { // 注意这个块
print("x is shen me gui")
}
}
- 3 在循环中,
java
经常会用到break
跳出循环,那kotlin
是怎么实现的呢?请看例子,可以通过直接标签的形式,回到指定位置,和java
类似
tag@ for (i in 0..100) {
for (j in 0..100) {
if (i==j)
break@tag
}
}
7. 数据类
相信java
的小伙伴从刚开始学java
的时候就知道建立对象先写个javabean
,要写get、set
方法,比较的时候要重写equal
等方法,copy
对象的时候要实现Cloneable
接口。非常的麻烦,而且容易出错。kotlin
给出了一种简单的方法
data class person(var age: int ,var name: String)
编绎器会自动使该Bean
拥有以下特性:
- 1
get/set
方法,当var
改为val
时,则没有set
方法 - 2
equals()
/hashCode()
- 3
toString()
方法,格式person(age=18, name=ivy)
- 4
copy()
拷贝功能
8. 静态变量、静态方法
在kotlin
中,没有静态方法的说法。都是通过伴生对象companion object
来实现。使用静态变量可以通过
const val staticName: String ="123"
来实现(必须写在类的顶部),一般用于无法确定归属的全局变量。但是最好使用伴生对象,对静态变量归类到所属的类中。使用方法如下
class TestActivity{
companion object{
var username: String="ivy"
fun startActivity(context: Context):Unit{
val intent=Intent(context,KotlinHomeActivity::class.java)
context.startActivity(intent)
}
}
}
//调用
TestActivity.companion.startActivity(context)
伴生对象并不是真正意义上的静态变量,本质也是一个对象
kotlin
的单例写法如下:
companion object{
fun getUser() : String{
return UserManager.user
}
private object UserManager{
val user=User()
}
}
9. 泛型
kotlin
的泛型和java
的类似,有以下几点不同:
- 1 可以通过
T::class
来获取到T
的具体类型 - 2
kotlin
不支持通配符,即class person(? extends T)
- 3 声明处型变,即如下所示,只控制一个类中只有返回值才有可能需要校验
T
泛型,输入是不会出现T
泛型这种情况的(同时也有<in T>
表示输入)
abstract class Person<out T> {
abstract fun getSomeThing(): T
}
那么这样有什么好处呢?看下面的代码,这在java
中不可行,是禁止这样操作的。但是实际上,这样是极为安全的
fun demo(strs: Person<String>) {
val objects: Person<Any> = strs
// ...
}
10. 代理
在kotlin
当中可以对类和属性进行代理
- 1 代理类(代理模式)
interface Person{
fun run()
}
class SuperMan() : Person{
override fun run() { print("i can fly") }
}
class Delegate(person: Person) : Person by person
fun main(args: Array<String>) {
val superMan = SuperMan()
Delegate(b).run() //打印 i can fly"
}
- 2 代理属性,这是一个超级棒的功能,这里面和很多功能,这里我就介绍两种
- 代理属性
class Example {
var p: String by Delegate()
}
class Delegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return 'someThing"
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
println("value'")
}
}
这个这可以在属性获取值和设置值的时候进行代理拦截,这个功能非常强大,例子如下 (代码来源《Kotlin for Android Developer》
)
class Preference<T>(val context: Context, val name: String, val default: T, val prefName: String = "default") : ReadWriteProperty<Any?, T> {
val prefs by lazy { context.getSharedPreferences(prefName, Context.MODE_PRIVATE) }
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
return findPreference(name, default)
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
putPreference(name, value)
}
private fun <U> findPreference(name: String, default: U): U = with(prefs) {
val res: Any = when (default) {
is Long -> getLong(name, default)
is String -> getString(name, default)
is Int -> getInt(name, default)
is Boolean -> getBoolean(name, default)
is Float -> getFloat(name, default)
else -> throw IllegalArgumentException("This type can be saved into Preferences")
}
res as U
}
private fun <U> putPreference(name: String, value: U) = with(prefs.edit()) {
when (value) {
is Long -> putLong(name, value)
is String -> putString(name, value)
is Int -> putInt(name, value)
is Boolean -> putBoolean(name, value)
is Float -> putFloat(name, value)
else -> throw IllegalArgumentException("This type can be saved into Preferences")
}.apply()
}
}
当使用get
方法时,会自动从sharePreference
当中取,当给变量设值的时候会自动给把值存储更新到sharePreference
当中,可以像使用正常变量一样使用sharePreference
,使用方法如下:
class TestActivity : Activity(){
var version: Int by Preference(this, "version", 1.0.0)
}
- 可观察属性(
observable properties
)
这个的作用,是当一个变量的值改变时,会进行通知,并告知旧值和新值。这个在开发中就非常实用了,比如定位功能,可以判断定位的值是否发生变化,设置了定位信息,从而允许接下来的逻辑。
var locationCity: String by Delegates.observable("<no location>") {
prop, old, new ->
println("$old -> $new")
}
在开发中我们通常会通过 一个helper
工具类来实现对某个对象的属性值来进行控制。比如,当设置的年龄小于15岁的时候不允许更改,在这里使用Delegates.observable()
就很方便了,而且逻辑看起来更加清晰明了。
也可以通过Delegates.vetoable()
来控制变量的值是否可以修改(使用方法和 Delegates.observable()
一样)
- 3 属性延迟
在开发中,我们经常会做一件事件,当一个对象需要使用时才初始化,不使用不初始化。这在kotlin
中就很方便实现,当第一次使用时才初始化,随后直接使用初始化后的值(该方法默认是同步的,如果不需要同步可以加上LazyThreadSafetyMode.NONE
)
val user:Student by lazy {
Student("girl",18,true)
}
11. 扩展
kotlin
的扩展是我最喜欢的一个新功能了,之前开发ios
的时候就喜欢得不得了,现在在Android
中终于可以使用了。在java
世界中每个开发者都有一堆自己的工具类,如StringUtils
、ViewUtils
、ToastUtils
等等。但是这样会有一个问题,比如View
的一个工具类,谁知道你的工具类名叫什么?就算知道,也很麻烦。比如设置paddlingLeft
的一般套路
ViewUtils.setPaddingLeft(view,paddingLeft)
但是通过扩展,就完成清晰明了,和调系统方法没有任务区别,首先编写一个扩展
fun View.setPaddingLeft(paddingLeftNew: Int){
setPadding(paddingLeftNew,paddingTop,paddingRight,paddingBottom)
}
//调用:
view.paddingLeft=12
是不是很赞!!!扩展功能非常强大,我们可以把各种工具类进行简化。比如Toast
fun Any.toast(text: String){
Toast.makeText(applicationContext,text,Toast.LENGTH_SHORT).show()
}
当然扩展不止于此,脑洞有多大,扩展就有多强!
12. 和ButterKnife say goodbye
每个Android
的开发者曾经都为findViewById()
而烦恼不已,直到后来ButterKnife
的出现。但是ButterKnife
是不是就完美了,并不是!当布局复杂的时候,activity
里面前面一堆View
的变量。那有没有办法去掉这一堆变量,有!
import kotlinx.android.synthetic.main.activity_kotlin.*
class Activity{
btnCommint.text="xxx"
}
activity_kotlin.xml
<Button
android:id="@+id/btnCommit"
android:layout_width="match_parent"
android:layout_height="40dp"
android:text="i am kotlin button"/>
只需要把import
布局文件,就可以直接把Id
当成变量来使用。轻轻松松和ButterKnife say goodbye
。
13. 一些小细节
- 1
Class
对象的获取
View::class.java //在kotlin中获取class对象传递到java代码中
View::javaClass //在kotlin中获取class对象传递到kotlin代码中
- 2
this
的使用
class KotlinActivity{
fun isBigNum(num: Int){
}
inner class Person{
open fun run(){
this@Person.run()
this@KotlinActivity.isBigNum(3)
}
}
}
- 3
SmartCast
(类型转换)
java:
TextView view=(TextView)findViewById(R.id.tv)
kotlin:
var view=findViewById(R.id.tv) as TextView
- 4 字符串拼接
java:
System.out.println("username:"+user.getUsername()+"--age:"+age)
kotlin:
print("username:${user.username}--age:$age)
- 5 三目运算符
在kotlin
当中没有三目运算符,实现三目运算符的方法:
var maxValue= if(a>b) a else b
网友评论