Android开发者都知道,Google宣布Kotlin First,并且在Android官网上的Demo代码都是Kotlin优先,之后提供的代码也是,推广的决心很大。而且Kotlin和Java能100%兼容,并且语法更简洁,后面我也会陆续写一些Kotlin的分享文章,今天先概览一下Kotlin的优势。
1.类型推断
// 只读变量声明(更友好) 想想final
val a: Int = 1 // 后置类型声明
// 一般利用类型推断,思维更加顺畅,不用再关心参数是什么类型的问题
val a = 5
val s = String()
val clazz = s.getClass()
val method = clazz.getDeclaredMethod("name", null)
// 可变变量声明
var x = 5
2.View初始化
inline fun <reified TV:View> KView(context: Context, init: TV.() -> Unit) : TV {
val constructor = TV::class.java.getConstructor(Context::class.java)
val view : TV = constructor.newInstance(context)
view.init()
return view
}
>>> usage
val view = KView<TextView>(this) {
layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT)
text = "Hello"
}
3.NUllPointer
空类型和非空类型
var age: String? = null
// 直接调用会编译报错
val age1 = age.toInt()
// 正确调用
val age1 = age?.toInt()
// 不做处理返回null
val age2 = age?.toInt()
// age为空返回-1
val age2 = age?.toInt() ?: -1
// 抛出空指针异常
val age2 = age!!.toInt()
val l = s?.length // s != null, l = s.length else l = null, l: Int?
val l = s!!.length // same as l = s.length in java, l: Int
val l = s?.length ?: 0 // s != null, l = s.length else l = 0, l: Int
return myValue ?: -1
// 链式使用:
bob?.department?.head?.name // 任一为null不执行
Attention:
如果被Java调用,由于Java无法保证非空(除非已经使用@NonNull
注解注明),从Java接收的参数必须是可空的。
4. ? and Elvis
// java
if(view != null) {
if(view.getParent() != null) {
if(view.getParent() instanceof ViewGroup) {
((ViewGroup) view.getParent()).removeView(view)
}
}
}
// kotlin
(view?.parent as? ViewGroup)?.removeView(view)
5. when
when(x) {
in 1..10 -> print("x is in the range")
!in 10..20 -> print("x is outside the range")
is String -> print("x is a string")
else -> print("none of the above")
}
// usage
if(TextUtils.equals(day, today)) {
xxx1
} else if(TextUtils.equals(day, tomorrow)) {
xxx2
} else {
xxx3
}
// kotlin
when(day) {
today -> xxx1
tomorrow -> xxx2
else -> xxx3
}
6. 扩展函数
private fun <T> MutableList<T>.swap(index1: Int, index2: Int) {
val tmp: T = this[index1]
this[index1] = this[index2]
this[index2] = tmp
}
>>> usage
val list = mutableListOf<Int>(1, 2, 3)
list.swap(0, 1)
println(list)
>>> out
[2, 1, 3]
7. 关键字object
单例
object KotlinPower {
override fun toString(): String {
return "KotlinPower"
}
}
>>> usage
println(KotlinPower.toString())
对象字面量
fun testObject() {
val a = object {
var x: Int = 0
var y: Int = 0
}
println("a.x: ${a.x}, a.y: ${a.y}")
}
>>> usage
testObject()
>>> out
a.x: 0, a.y: 0
再看一个例子:
object Test {
fun sayMessage(msg: String) {
println("from Kotlin $msg")
}
}
// kotlin code
Test.sayMessage("Hello")
// java code
Test.INSTANCE.sayMessage("hello");
如果要保持java和Kotlin中的调用方式一样,可以在Kotlin中通过注解@JvmStatic方式
object Test {
@JvmStatic
fun sayMessage(msg: String) {
println("from Kotlin $msg")
}
}
//这样在java中就可以直接
Test.sayMessage("Hello")
8. 属性
引入了属性的概念,隐式的getter
与setter
:
var stringRepresentation: String
get() = this.toString()
set(value) {
setDataFromString(value)
}
// 相当于
public String getStringRepresentation() {
return this.toString();
}
public void setStringRepresentation(String value) {
setDataFromString(value)
}
9.函数
在Kotlin中函数成为了一等公民,位置不需要一定在类的内部,可以和类成为顶级函数,函数也可以作为参数传递
- 缺省参数,命名参数
可以简化重载,不用再使用Builder等优势
fun reformat(str: String,
normalizeCase: Boolean = true,
upperCaseFirstLetter: Boolean = true,
divideByCamelHumps: Boolean = false,
wordSeparator: Char = ' ') {
// do something
}
reformat(str)
reformat(str, wordSeparator = ' ') // 可以使用参数的名字给缺省参数赋值
// 可以通过@JvmOverloads注解生成Java的重载形式便于Java来调用
- 扩展函数
可以去掉Java中到处存在的XXXUtils.java类
// Java
// 写一个Util类,作为参数传进去
public class ViewUtils {
public static int findColor(View view, int resId) {
return view.getResources().getColor(resId);
}
}
ViewUtils.findColor(view, resId);
使用Kotlin简化如下:
fun View.findColor(id: Int) : Int {
return this.resources.getColor(id)
}
view.findColor(resId)
一系列这种类型的Java工具类在Kotlin中被“改造”成了扩展方法例如:
Collection.sort(list)
在Kotlin中直接list.sort()
就可以了。
可以完全取代以往的Util
类。
10.作用域函数
- let/run
对象作为参数传入lambda
(run
则作为this
),返回值为lambda
表达式的返回值, 常见场景:转换类型,处理nullable
类型
// if...else...写法
private fun testIfElse(): Object? {
return if (a !== null) {
val b = handleA(a)
if (b !== null) {
handleB(b)
} else {
null
}
} else {
null
}
}
// ?.let写法
private fun testLet(): Object? {
return a?.let { handleA(it) }?.let { handleB(it) }
}
- apply
对象作为this
传入lambda
, 返回值为对象本身,常见场景:初始化对象
- also
对象作为参数传入lambda
,返回值为对象本身,常见场景:链式调用中的副作用
// transforming data from api with intermediary variable
val rawData = api.getData()
Log.debug(rawData)
rawData.map { /** other stuff */ }
// use 'also' to stay in the method chains
api.getData()
.also { Log.debug(it) }
.map { /** other stuff */ }
- takeIf/takeUnless
对象作为参数传入lambda
,返回值为对象本身或null
(根据lambda
中语句的true or false
),常见场景:链式调用形式的条件判断
File(url).takeIf { it.exists() }
?.let {
JSONObject(NetworkUtils.postFile(20 * 1024, "http://i.snssdk.com/2/data/upload_image/", "image", url))
}?.takeIf { it.optString("message") == "success" }
?.let {
post(content, contact, it.optJSONObject("data")?.optString("web_uri"))
} ?: mHandler.post { view?.onFail() }
11.Lambda
本质上是一个匿名方法(单方法接口)
fun isGreaterThanZero(n: Int): Boolean {
return n > 0
}
collection.filter(::isGreaterThanZero)
// 使用Lambda
collection.filter{ i: Int -> i > 0 }
collection.filter{ i -> i > 0 }
collection.filter{ it > 0 }
单方法接口都可以传Lambda
:
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
showToast();
}
});
button.setOnClickListener{ showToast() }
内置常用的流处理Lambda
:
names
.filter{ it.startsWith("A") }
.sortedBy{ it }
.map{ it.toUpperCase() }
.forEach{ print(it) }
12.其他
- 支持运算符重载
- 接口可以有缺省方法
-
Data Class
数据类型,自动实现`equals/hashCode/toString - 协程
- 伴生对象
Refs: