本文的demo地址(https://github.com/dingjinwen/kotlin_tips)
Tip1-Kotlin和Java相互查看
1. Kotlin轉(zhuǎn)化為Java代碼查看
kotlin文件是不能直接轉(zhuǎn)化為Java文件的抒和,但是可以將Kotlin代碼轉(zhuǎn)化成Java語言去理解歉眷,步驟如下:在Android Studio中選擇Tools ---> Kotlin ---> Show Kotlin Bytecode 這樣就把Kotlin轉(zhuǎn)化為Class字節(jié)碼了,Class字節(jié)碼閱讀不太友好,點(diǎn)擊左上角的Decompile就轉(zhuǎn)化為Java
2. Java文件直接轉(zhuǎn)化為Kotlin文件
再介紹一個(gè)小竅門,在前期對Kotlin語法不熟悉的時(shí)候,可以先用Java寫好代碼,再利用AndroidStudio工具將Java代碼轉(zhuǎn)化為Kotlin代碼,步驟如下:在Android Studio中選中要轉(zhuǎn)換的Java代碼 ---> 選擇Code ---> Convert Java File to Kotlin File
Tip2-屬性
1. 什么時(shí)候?qū)傩灶愋筒荒苁÷裕?/h3>
a. 屬性的修飾符储狭,和方法類似的,屬性也有多種修飾符(public,protected,private)捣郊。
如果可以根據(jù)屬性的值推斷出屬性類型辽狈,則可省略類型,比如:
var aa: String = ""
b. 不能根據(jù)屬性的值推斷出屬性類型,則不可省略類型,比如:
abstract修飾的屬性,自身不能初始化模她,要在子類進(jìn)行初始化稻艰,不能省略類型
lateinit延遲初始化的,在使用之前再初始化的侈净,不能省略類型
2. 屬性的組成部分
a. 屬性的修飾符储狭,和方法類似的,屬性也有多種修飾符(public,protected,private)捣郊。
如果可以根據(jù)屬性的值推斷出屬性類型辽狈,則可省略類型,比如:
var aa: String = ""
b. 不能根據(jù)屬性的值推斷出屬性類型,則不可省略類型,比如:
abstract修飾的屬性,自身不能初始化模她,要在子類進(jìn)行初始化稻艰,不能省略類型
lateinit延遲初始化的,在使用之前再初始化的侈净,不能省略類型
對屬性的訪問尊勿,并不是像Java里面一樣僧凤,直接訪問屬性的本身,而是默認(rèn)調(diào)用了 get 和 set 方法元扔,保證了屬性的閉合性. 一般屬性包含三個(gè)部分躯保,set , get 和 backing filed.
var aa: String = ""
set(value) {
field = value //field是(Backing field)幕后字段
}
get() {
return field
}
3. 自定義set 和 get 方法
var,val修飾的變量默認(rèn)是 public 的,編譯器會自動(dòng)生成 set 和 get 方法,也可以手動(dòng)重寫他的 set 和 get 方法,val只有 get 方法
var aa: String = "ding jin wen "
set(value) {
field = value + " 123 "
}
get() {
return field + "大地零一"
}
Log.d("text", "aa : $aa")
aa = "zhang san"
Log.d("text", "aa : $aa")
//輸出
aa:ding jin wen 大地零一
aa:zhang san 123 大地零一
4. 幕后字段(Backing Field)
kotlin的 get和 set 是不允許訪問本身的局部變量的澎语,因?yàn)閷傩缘恼{(diào)用也是對get的調(diào)用途事,因此會產(chǎn)生遞歸,造成內(nèi)存溢出擅羞。
5. 延遲初始化lateinit 和 懶初始化by lazy
lateinit var aa:String
a.lateinit只能用于var聲明的類變量尸变,并且屬性沒有自定義get或set方法。
b.屬性的類型必須是非空的
val aa:String by lazy { }
a.lazy只能作用在val屬性,應(yīng)用于單例模式,當(dāng)且僅當(dāng)屬性被第一次調(diào)用的時(shí)候,委托方法才會執(zhí)行减俏。
b.lazy()是接受一個(gè) lambda 并返回一個(gè) Lazy <T> 實(shí)例的函數(shù),返回的實(shí)例可以作為實(shí)現(xiàn)延遲屬性的委托, 第一次調(diào)用 get() 會執(zhí)行已傳遞給 lazy() 的 lambda 表達(dá)式并記錄結(jié)果,后續(xù)調(diào)用 get() 只是返回記錄的結(jié)果召烂。
6. 編譯器常數(shù)值
如果在編譯期間,屬性值就能被確定娃承,該類屬性值使用const 修飾符奏夫,類似Java里面的靜態(tài)常量用法。 這類屬性必須滿足以下條件:
a. 必須是頂級屬性历筝,或者是一個(gè)object的成員
b. 值被初始化為 String 類型酗昼,或基本類型(primitive type)
c. 只能修飾val常量
d. 不存在自定義的取值方法
const val key = "key"
object Config {
const val name="name"
}
7. 委托屬性
有一種屬性,在使用的時(shí)候每次都要手動(dòng)實(shí)現(xiàn)它梳猪,但是可以做到只實(shí)現(xiàn)一次麻削,并且放到庫中,一直使用春弥,這種屬性稱為委托屬性碟婆。
委托屬性包括:
a. 延遲屬性(lazy properties):上面第5點(diǎn)中已經(jīng)提到了。
b. 可觀察屬性(observable properties):監(jiān)聽得到屬性變化通知惕稻。
//看下面例子,快速雙擊退出頁面
var mBackPressedTime by Delegates.observable(0L) {
prop, old, new ->//三個(gè)參數(shù)蝙叛,分別是:被賦值的屬性俺祠,舊值和新值。
if (new - old > 1000) {
Toast("再按一次返回就退出")
}
if (new - old in 1..1000) {
finish()
}
}
override fun onBackPressed() {
mBackPressedTime = System.currentTimeMillis()
}
c. Map委托屬性(Storing Properties in a Map):將所有屬性存在Map中借帘。
class Person2(private val maps: Map<String, String>) {
val name: String by maps
val company: String by maps
val address: String by maps
val email: String by maps
}
fun main(args: Array<String>) {
val data = mapOf("name" to "Jack", "company" to "JetBrains")
val p = Person2(data)
println(p.name) // Jack
}
Tip3-函數(shù)
1. 變參函數(shù)
//在Java中蜘渣,我們這么表示一個(gè)變長函數(shù)
public boolean hasEmpty(String... strArray){
for (String str : strArray){
if ("".equals(str) || str == null)
return true;
}
return false;
}
//在Kotlin中,使用關(guān)鍵字vararg來表示
fun hasEmpty(vararg strArray: String?): Boolean{
for (str in strArray){
if (str.isNullOrEmpty())
return true
}
return false
}
2. 擴(kuò)展函數(shù)
- 聲明一個(gè)擴(kuò)展函數(shù)肺然,我們需要在函數(shù)的名稱前加上一個(gè)接收者類型并且加上.符號,在擴(kuò)展函數(shù)中的this關(guān)鍵字表示接收者對象
fun Activity.toast(message: CharSequence, duration: Int = Toast.LENGTH_SHORT) {
Toast.makeText(this, message, duration).show()
}
- 擴(kuò)展是靜態(tài)解析
open class Animal
class Dog : Animal()
fun Animal.bark() = "animal"
fun Dog.bark() = "dog"
fun printBark(animal: Animal) {
println(animal.bark())
}
printBark(Animal()) 和 printBark(Dog())打印的都是 animal ,因?yàn)樵诙x擴(kuò)展函數(shù)的時(shí)候接收對象類型是Animal類蔫缸,而Kotlin的擴(kuò)展是靜態(tài)解析的,所以即使調(diào)用的時(shí)候是傳了Animal類的子類Dog類進(jìn)去际起,還是會執(zhí)行定義的時(shí)候的類型的函數(shù)拾碌。
3. 構(gòu)造函數(shù)
kotlin里面的構(gòu)造函數(shù)吐葱,分為主構(gòu)造函數(shù)和次構(gòu)造函數(shù),主構(gòu)造函數(shù)寫在類頭中,跟在類名后面,次構(gòu)造函數(shù)寫在類體中,關(guān)鍵字是constructor
,次構(gòu)造函數(shù)必須直接或者間接地委托到主構(gòu)造函數(shù)
class Student(private var name: String) {
private var age: Int? = null
private var classId: Int? = null
constructor(name: String, age: Int) : this(name) {
this.age = age
this.name = name
}
constructor(classId: Int, name: String, age: Int) : this(name, age) {
this.classId = classId
this.age = age
this.name = name
}
fun sayHello() {
Log.e("test", "hello $name")
Log.e("test", "hello $classId")
Log.e("test", "hello $age")
}
}
btn.setOnClickListener {
Student(1, "zhang san", 20).sayHello()
// 打印test: hello zhang san
// 打印test: hello 1
// 打印test: hello 20
}
4. 單例怎么寫校翔?
- 不帶參數(shù)(兩種寫法)
- 伴生對象更多的用途是用來創(chuàng)建一個(gè)單例類弟跑。只是簡單的寫,直接用伴生對象返回一個(gè)val修飾的外部類對象就可以了
class Single private constructor() {
companion object {
val instance = Single()
}
}
- 但是更多的時(shí)候我們希望在類被調(diào)用的時(shí)候才去初始化他的對象防症。以下代碼將線程安全問題交給虛擬機(jī)在靜態(tài)內(nèi)部類加載時(shí)處理孟辑,是一種推薦的寫法:
class Single private constructor() {
companion object {
val instance: Single by lazy { Holder.INSTANCE }
}
private object Holder {
val INSTANCE = Single()
}
}
- 帶參數(shù)
/**
* 簡單寫法
*/
class Single private constructor(name:String) {
companion object {
@Volatile
private val instance:Single?=null
fun getInstance(c:String):Single{
if (instance == null) {
synchronized(Single::class) {
if (instance == null) {
instance = Singleton(c)
}
}
}
return instance!!
}
}
}
/**
* 帶參數(shù)的單例,去掉斷言蔫敲,google推薦的寫法
*/
class SingleInstance private constructor(name: String) {
companion object {
@Volatile
private var instance: SingleInstance? = null
fun getInstance(name: String): SingleInstance {
return instance ?: synchronized(this) {
instance ?: SingleInstance(name).also {
instance = it
}
}
}
}
}
5. 靜態(tài)方法
Kotlin 沒有靜態(tài)方法,在項(xiàng)目中常用的兩種方法:
- 用@jvmStatic注解,和Java的static類似
object StringUtils {
@JvmStatic fun isEmpty(str: String): Boolean {
return "" == str
}
}
- 使用伴生對象
class StringUtils {
companion object {
fun isEmpty(str: String): Boolean {
return "" == str
}
}
}
6. let,apply,with,run,also的用法和區(qū)別
先看下面這個(gè)例子饲嗽,打印字母表函數(shù)
/*
*打印字母表函數(shù),在函數(shù)內(nèi)result變量在好幾處有使用到
*/
fun alphabet(): String {
val result = StringBuilder()
result.append("START\n")
for (letter in 'A'..'Z') {
result.append(letter)
}
result.append("\nEND")
return result.toString()
}
在上面的函數(shù)中奈嘿,result變量出現(xiàn)了5次貌虾,下面分別用apply,with,let來簡化這個(gè)函數(shù),可以將這5次都不用再出現(xiàn)了
- apply用法:
/**
* 打印字母表函數(shù)指么,apply函數(shù)酝惧,調(diào)用某對象的apply函數(shù),在函數(shù)范圍內(nèi)伯诬,可以任意調(diào)用該對象的任意方法晚唇,并返回該對象
*/
private fun alphabetApply(): String {
// fun <T> T.apply(f: T.() -> Unit): T { f(); return this }
return StringBuilder().apply {
append("START\n")
for (letter in 'A'..'Z') {
append(letter)
}
append("\nEND")
}.toString()
}
btn.setOnClickListener {
Log.d("test", StringUtils.alphabetApply())
}
//打印
//START
//ABCDEFGHIJKLMNOPQRSTUVWXYZ
//END
- with用法:
/**
* 打印字母表函數(shù),with函數(shù)是一個(gè)單獨(dú)的函數(shù)盗似,并不是Kotlin中的extension哩陕,所以調(diào)用方式有點(diǎn)不一樣,返回是最后一行
*/
private fun alphabetWith(): String {
// fun <T, R> with(receiver: T, f: T.() -> R): R = receiver.f()
return with(StringBuilder()) {
append("START\n")
for (letter in 'A'..'Z') {
append(letter)
}
append("\nEND")
toString()
}
}
btn.setOnClickListener {
Log.d("test", StringUtils.alphabetWith())
}
//打印
//START
//ABCDEFGHIJKLMNOPQRSTUVWXYZ
//END
- let用法:
/**
* 打印字母表函數(shù)赫舒,let函數(shù)悍及,默認(rèn)當(dāng)前這個(gè)對象作為閉包的it參數(shù),返回值是函數(shù)里面最后一行接癌,或者指定return
* XX?.let{}這種寫法在代碼中心赶,比較常見
* XX?:let{}
*/
private fun alphabetLet(): String {
// fun <T, R> T.let(f: (T) -> R): R { f(this)}
return StringBuilder().let {
it.append("START\n")
for (letter in 'A'..'Z') {
it.append(letter)
}
it.append("\nEND")
it.toString()
// return it.toString()
}
}
btn.setOnClickListener {
Log.d("test", StringUtils.alphabetLet())
}
//打印
//START
//ABCDEFGHIJKLMNOPQRSTUVWXYZ
//END
- also用法:
/**
* also()函數(shù)和let()函數(shù)很像,但是返回值為該對象自己
* 字面理解:做缺猛。缨叫。。的同時(shí)也做荔燎。耻姥。。
*/
fun testAlso() {
// fun <T> T.also(block: (T) -> Unit): T { block(this); return this }
"testAlso".apply {
println("this = " + this)
}.also { println(it) }
}
btn.setOnClickListener {
Log.d("test", StringUtils.testAlso())
}
//打印
//System.out: this = testAlso
//System.out: testAlso
- run用法:
/**
* run函數(shù)和apply函數(shù)很像有咨,只不過run函數(shù)是使用最后一行的返回琐簇,apply返回當(dāng)前自己的對象。
*/
fun testRun() {
// fun <T, R> T.run(f: T.() -> R): R = f()
"testRun".run {
println("this = " + this)
}.let { println(it) }
}
btn.setOnClickListener {
Log.d("test", StringUtils.testRun())
}
//打印
//System.out: this = testRun
//System.out: kotlin.Unit
8.中綴函數(shù)
中綴表達(dá)式是操作符以中綴形式處于操作數(shù)的中間(例:3 + 4)座享,先來看一下Kotlin中的中綴函數(shù):
- 在
mapOf()
方法中的to
就是個(gè)中綴函數(shù)
// public infix fun <A,B> A.to(that:B):Pair<A,B> = Pair(this,that)
val map: Map<Int,Int> = mapOf(1 to 1,2 to 2)
- Range里面的
downTo
也是個(gè)中綴函數(shù):
(10 downTo 1).forEach{print(it)}
使用中綴符號infix修飾函數(shù)婉商,但必須符合一些條件:
- 使用infix關(guān)鍵字表示
- 必須是成員方法或者擴(kuò)展函數(shù)
- 函數(shù)只有一個(gè)參數(shù)
下面來寫個(gè)中綴函數(shù):
// 定義擴(kuò)展函數(shù)
infix fun Int.add(x: Int): Int = this + x
fun main(args: Array<String>) {
// 用中綴符號表示的擴(kuò)展函數(shù)
println("2 add 1:${2 add 1}") // 打铀朴恰:2 add 1:3
// 與下面是相同的
println("2.add(1):${2.add(1)}") // 打印:2.iInfix(1):3
}
Tip4-自定義屬性委托
1. 只讀屬性實(shí)現(xiàn)委托
只讀屬性(使用val定義),委托類需實(shí)現(xiàn)getValue函數(shù)
interface ReadOnlyProperty<in R, out T> {
operator fun getValue(thisRef: R, property: KProperty<*>): T
}
2. 可變屬性實(shí)現(xiàn)委托
可變屬性(使用var定義),委托類需實(shí)現(xiàn)getValue函數(shù)和setValue函數(shù)
interface ReadWriteProperty<in R, T> {
operator fun getValue(thisRef: R, property: KProperty<*>): T
operator fun setValue(thisRef: R, property: KProperty<*>, value: T)
}
3. 一個(gè)委托實(shí)例
下面來看一個(gè)自定義的Delegate
据某,用來訪問SharedPreference
橡娄,這段代碼是Kotlin for Android Developer
的示例:
class Preference<T>(val context: Context, val name: String, val default: T) : ReadWriteProperty<Any?, T> {
val prefs by lazy { context.getSharedPreferences("default", 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()
}
}
使用的時(shí)候:
class ExampleActivity : AppCompatActivity(){
var a: Int by Preference(this, "a", 0)
fun whatever(){
println(a)//會從SharedPreference取這個(gè)數(shù)據(jù)
aInt = 9 //會將這個(gè)數(shù)據(jù)寫入SharedPreference
println(a)//會從SharedPreference取這個(gè)數(shù)據(jù)
}
}
Tip5-委托模式
Kotlin支持委托模式,與java的動(dòng)態(tài)代理類似癣籽,有兩個(gè)對象參與處理同一個(gè)請求挽唉,接受請求的對象將請求委托給另一個(gè)對象來處理。java里面動(dòng)態(tài)代理是用反射來實(shí)現(xiàn)的筷狼,還要添加額外的很多代碼瓶籽,相比較kotlin的動(dòng)態(tài)代理就簡單很多了。
interface Animal{
fun bark()
}
class Dog :Animal {
override fun bark() {
Log.e("test","Wang Wang")
}
}
class Cat(animal: Animal) : Animal by animal {
}
fun main(args: Array<String>) {
Cat(Dog()).bark()
}
//輸出:wangwang
//這個(gè)實(shí)例中埂材,用狗代理了貓,幫貓?zhí)幚砹私新暤牟僮?
class CountingSet2<T>(val innerSet: MutableCollection<T> = HashSet<T>()) :MutableCollection<T> by innerSet{
var objectAdded = 0
init {
objectAdded += innerSet.size
}
override fun add(element: T): Boolean {
objectAdded++
return innerSet.add(element)
}
override fun addAll(elements: Collection<T>): Boolean {
objectAdded += elements.size
return innerSet.addAll(elements)
}
}
val hashSet = hashSetOf("1", "2", "3", "4")
val set = CountingSet2(hashSet)
set.addAll(listOf("5", "6", "7"))
Log.e("test", "set-----${set.size}----${set.objectAdded}")
//輸出:set-----7----7
//這個(gè)實(shí)例中塑顺,用innerSet代理了CountingSet2,幫CountingSet2處理了add,addAll的操作
Tip6-Lambda表達(dá)式與高階函數(shù)
1. Lambda 表達(dá)式的語法
一個(gè)Lambda表達(dá)式通常使用{ }包圍,參數(shù)是定義在()內(nèi)俏险,可以添加類型注解严拒,實(shí)體部分跟在“->”后面;如果Lambda的推斷返回類型不是Unit竖独,那么Lambda主體中的最后一個(gè)(或單個(gè))表達(dá)式將被視為返回值裤唠。
先來看下面的例子:
val sum: (Int, Int) -> Int = { x, y -> x + y }
// val printMsg: (String) -> Unit = { msg: String -> println(msg)}
val printMsg = { msg: String ->
println(msg)
}
fun main(args: Array<String>) {
sum(2,3)
printMsg.invoke("hello")
}
//輸出:hello
2. Lambda 表達(dá)式的約定
- 當(dāng)參數(shù)只有一個(gè)的時(shí)候莹痢,聲明中可以不用顯示聲明參數(shù)种蘸,在使用參數(shù)時(shí)可以用 it 來替代那個(gè)唯一的參數(shù)。
class Num {
fun oneParams(one : (Int) -> Int){
println("oneParams : ${one(5)}")
}
}
fun main(args : Array<String>){
val num = Num()
// num.oneParams({ it -> it * 2 })
num.oneParams{it * 2}
}
// 輸出oneParams : 10
- 當(dāng)有多個(gè)用不到的參數(shù)時(shí)竞膳,可以用下劃線來替代參數(shù)名(1.1以后的特性)航瞭,但是如果已經(jīng)用下劃線來省略參數(shù)時(shí),是不能使用 it 來替代當(dāng)前參數(shù)的坦辟。
class Num {
fun unusedParams(unused : (Int,Int) -> Int){
println("unusedParams : ${unused(5,10)}")
}
}
fun main(args : Array<String>){
val num = Num()
num.unusedParams { _, used -> used * 2 }
// num.unusedParams { _, used -> it * 2 } 這種寫法是編譯不過的刊侯,不能使用 it 來替代當(dāng)前參數(shù)的。
}
// 輸出 unusedParams : 20
- 如果函數(shù)的最后一個(gè)參數(shù)是一個(gè)函數(shù)锉走,那么我們在用Lambda表達(dá)最后一個(gè)函數(shù)參數(shù)的時(shí)候滔吠,可以把它放在括號()外面,所以下面的寫法是等價(jià)的挠日。
class Num {
fun logic(a: Int, b: Int, calc: (Int, Int) -> Int){
println("calc : ${calc(a,b)}")
}
}
fun main(args : Array<String>){
val num = Num()
// num.logic(1, 2,{x,y -> x+y})
num.logic(1, 2){x,y -> x+y}
}
- Lambda 最后一條語句的執(zhí)行結(jié)果表示這個(gè) Lambda 的返回值。
// 寫法2
num.unusedParams { _, used ->
println("print first")
used * 2
// 下面這種寫法是等價(jià)的
// return@unusedParams used * 2
}
- Lambda表達(dá)式來簡化OnClickListener的寫法:
interface OnClickListener {
fun onClick()
}
class View {
var listener: OnClickListener? = null;
/*
* 傳統(tǒng)方式
*
*/
fun setOnClickListener(listener: OnClickListener) {
this.listener = listener
}
fun doSth() {
// some case:
listener?.onClick()
}
// 但是這種方式僅適用于有一個(gè)回調(diào)函數(shù)的情況
/*
* 聲明lambda方式翰舌,listener: () -> Unit
* 函數(shù)可以是一種類型,一個(gè)變量可以是函數(shù)類型的
*/
var listener1:()->Unit
fun setOnClickListener(listener: () -> Unit) {
this.listener1=listener
}
fun doSth() {
// some case:
listener1?.invoke()
}
}
3. 高階函數(shù)
- Lambda 表達(dá)式最大的特點(diǎn)是可以作為參數(shù)傳遞嚣潜。當(dāng)定義一個(gè)閉包作為參數(shù)的函數(shù),稱這個(gè)函數(shù)為高階函數(shù)椅贱。(通常情況下懂算,我們所說的閉包是 Lambda 表達(dá)式)
fun main(args: Array<String>) {
log("world", printMsg)
}
val printMsg = { str: String ->
println(str)
}
val log = { str: String, printLog: (String) -> Unit ->
printLog(str)
}
//輸出:world
//log 有兩個(gè)參數(shù)只冻,一個(gè)str:String,一個(gè)printLog: (String) -> Unit计技。
- 函數(shù)作為返回值
函數(shù)作為返回值也非常實(shí)用喜德,例如我們的需求是根據(jù)不同的快遞類型返回不同計(jì)價(jià)公式,普通快遞和高級快遞的計(jì)價(jià)規(guī)則不一樣垮媒,這時(shí)候我們可以將計(jì)價(jià)規(guī)則函數(shù)作為返回值:
enum class Delivery {
STANDARD, EXPEDITED
}
/*
* 根據(jù)不同的運(yùn)輸類型返回不同的快遞方式
* */
fun getShippingCostCalculator(delivery: Delivery): (Int) -> Double {
if (delivery == Delivery.EXPEDITED) {
return { 6 + 2.1 * it }
}
return { 1.3 * it }
}
fun test05() {
val calculator1 = getShippingCostCalculator(Delivery.EXPEDITED)
val calculator2 = getShippingCostCalculator(Delivery.STANDARD)
println("Ex costs ${calculator1(5)}")
println("St costs ${calculator2(5)}")
}
如果是普通快遞舍悯,采用1.3 * it的規(guī)則計(jì)算價(jià)格,如果是高級快遞按照6 + 2.1 * it計(jì)算價(jià)格睡雇,根據(jù)不同的類型返回不同的計(jì)價(jià)函數(shù)萌衬。
Tip7-數(shù)據(jù)類
1. 用法和重要方法介紹
data class User(var name: String, var age: Int)
會自動(dòng)生成getter
和setter
方法,還有componentN()
它抱,對應(yīng)按聲明順序出現(xiàn)的所有屬性秕豫,如name
就是component1()
,age
就是component2()
观蓄。
當(dāng)然還有equals()
混移、hashCode()
、和toString()
(輸出的格式為User(name=..., age=...))如果構(gòu)造函數(shù)參數(shù)中沒有聲明是val或者var侮穿,這些函數(shù)就不會生成
主構(gòu)造函數(shù)需要有至少一個(gè)參數(shù)
數(shù)據(jù)類不能有abstract歌径、open、sealed和inner修飾
在1.1版本之前撮珠,數(shù)據(jù)類只能實(shí)現(xiàn)接口
在構(gòu)造函數(shù)那里也說過沮脖,如果生成的類需要一個(gè)無參數(shù)的構(gòu)造函數(shù),則必須指定所有屬性的默認(rèn)值
data class User(var name: String = "lily", var age: Int = 0)
eg.比如適用fastjson解析json串為對象時(shí)芯急,要求數(shù)據(jù)類必須有一個(gè)無參的構(gòu)造函數(shù)勺届,就要用到上面的寫法了復(fù)制
(copy)
數(shù)據(jù)類在創(chuàng)建的時(shí)候,除了會生成上面的幾個(gè)方法外娶耍,還會生成一個(gè)copy()
函數(shù)免姿,copy()
能夠復(fù)制一個(gè)對象改變它的一些屬性情況下,又要保持其余的不變榕酒,如上面的User
類胚膊,copy()
函數(shù)的實(shí)現(xiàn):
fun copy(name: String = this.name, age: Int = this.age) = User(name, age)
- 在使用copy()之后,就可以修改數(shù)據(jù)類的一些屬性了:
val jack = User(name = "jack", age = 1)
val olderJack = jack.copy(age = 2)
2.Pair和Triple
Kotlin提供了Pair
和Triple
作為標(biāo)準(zhǔn)數(shù)據(jù)類想鹰,命名數(shù)據(jù)類是更好的設(shè)計(jì)選擇紊婉。
兩個(gè)參數(shù)的時(shí)候使用Pair數(shù)據(jù)類
三個(gè)參數(shù)的時(shí)候使用Triple數(shù)據(jù)類
/**
* 元組(Tuple),給多個(gè)變量同時(shí)賦值辑舷,分二元(Pair)和三元(Triple)
*/
val (year, month, day) = Triple(2017, "6月", "14號")
Log.e("test", "${year}年$month$day")
val date = Triple(2017, "6月", "14號")
Log.e("test", "${date.first}年${date.second}${date.third}")
//二元同上喻犁,把Triple換成Pair
3.解構(gòu)聲明
在Kotlin中創(chuàng)建變量的話是這樣的
data class Person(var name: String, var age: Int)
fun main(args: Array<String>) {
val person = Person("jowan", 1)
var name = person.name
var age = person.age
println(name) // 打印jowan
println(age) // 打印1
}
- 使用解構(gòu)變量,同時(shí)創(chuàng)建多個(gè)變量
data class Person(var name: String, var age: Int)
fun main(args: Array<String>) {
val (name, age) = Person("person", 1)
println(name) // 打印person
println(age) // 打印1
}
Anko
Anko是 JetBrains 公司開發(fā)的一個(gè)強(qiáng)大的庫,主要的目的是用來替換之前用XML的方式肢础,來使用代碼生成UI布局
1. Anko四個(gè)組成部分內(nèi)容
- Anko Commons
輕量級的一些幫助類还栓,比如 intent,dialog传轰,logging 等等剩盒,其實(shí)就是對安卓一些類:Activity、Fragment慨蛙、Intent 等添加擴(kuò)展函數(shù)辽聊。 - Anko Layouts
動(dòng)態(tài)布局用的最主要的庫,將許多 Android 的控件 View 轉(zhuǎn)換成了 Anko 加載的形式股淡。
由于 Android 還有其他的控件庫身隐,因此 Anko 也對那些庫進(jìn)行了拓展支持,可以選擇添加對應(yīng)的依賴庫唯灵。
當(dāng)然贾铝,還可以根據(jù)需要對自定義 View 進(jìn)行改造,讓它們也支持 Anko 加載的形式埠帕。 - Anko SQLite
用于 Android SQLite 數(shù)據(jù)庫的查詢的庫 - Anko Coroutines
基于 kotlinx.coroutines 協(xié)程的一個(gè)工具庫垢揩。
2. Anko用法示例
- Anko Toast的簡單用法
toast("大地零一")
longToast("大地零一")
- Anko Dialog的簡單用法
alert("確定刪除嗎?"){
yesButton { toast("確定") }
noButton { toast("取消") }
}.show()
- Anko Intent的簡單用法
startActivity(intentFor<MainActivity().singleTop())
- Anko Coroutines
Anko還提供了協(xié)程的用來做一些耗時(shí)的操作,提供的操作為bg{},具體代碼如下:
async(UI){//UI線程
val data: Deferred<MyBean> = bg {//后臺線程
// Runs in background
MyBean()
}
showData(data.await()) //await方法將一直等待bg返回的數(shù)據(jù)
}
為了防止內(nèi)存泄漏我們常會使用弱引用敛瓷,在Anko中使用弱引用方法如下
val ref: Ref<AnkoActivity05> = this.asReference()
async(UI){
//ref替代了this@AnkoActivity05
ref().showData()
}
- Anko Layout
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:padding="30dp"
android:layout_width="match_parent">
<EditText
android:id="@+id/todo_title"
android:layout_width="match_parent"
android:layout_heigh="wrap_content"
android:hint="@string/title_hint" />
<!-- Cannot directly add an inline click listener as onClick delegates implementation to the activity -->
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/add_todo" />
</LinearLayout>
用 Anko 描述的同樣的視圖
verticalLayout {
padding = dip(30)
var title = editText {
id = R.id.todo_title
hintResource = R.string.title_hint
}
button {
textResource = R.string.add_todo
onClick { view -> {
// do something here
title.text = "Foo"
}
}
}
}
3. 不足
Anko 好是好叁巨,但是依舊不夠完美。
在 XML 中能夠設(shè)置的控件屬性更多呐籽,更精確的控制布局狀態(tài)锋勺,而 Anko 在構(gòu)建簡單界面的時(shí)候才顯得快速、便捷狡蝶。
而且 Anko 支持的控件有限庶橱,加載自定義的控件還得添加額外的代碼,在更復(fù)雜的應(yīng)用中應(yīng)該不太會廣泛的使用贪惹。
總結(jié)
- Kotlin是一門相對比較新的JVM語言苏章,JetBrains自2011年以來一直在積極地開發(fā)。多年來奏瞬,該語言在Android社區(qū)受到的關(guān)注度越來越高枫绅,并在Google IO 2017大會之后成為Android開發(fā)領(lǐng)域最熱門的話題。這次大會宣布硼端,Android正式支持Kotlin并淋。Kotlin在Java以及多種語言的基礎(chǔ)上,去掉了冗余代碼珍昨,代碼更加簡潔县耽,可讀性更強(qiáng)订咸,Kotlin還為已有的Java類提供一組好用的擴(kuò)展,絕大部分Java能實(shí)現(xiàn)的功能kotlin也是可以實(shí)現(xiàn)的,以后開發(fā)中酬诀,推薦大家能使用Kotlin的地方盡量使用。