Android為啥要從Java轉(zhuǎn)向Kotlin
Kotlin 是一種在 Java 虛擬機(jī)上運(yùn)行的靜態(tài)類型編程語(yǔ)言弓候,被稱之為 Android 世界的Swift晒哄,由 JetBrains 設(shè)計(jì)開(kāi)發(fā)并開(kāi)源当编。蘋(píng)果公司已經(jīng)在用Swift語(yǔ)言替代Object-C語(yǔ)言,Google也找到了替代Java的語(yǔ)言,也就是JetBrains公司(Android Studio也是用該公司的Intelli J改的)主推的Kotlin。現(xiàn)在將kotlin作為了編寫(xiě)Android的官方語(yǔ)言欢摄,而后會(huì)有越來(lái)越多的項(xiàng)目使用Kotlin。Kotlin有了強(qiáng)有力的親爸JetBrains公司和Google這個(gè)干爸笋粟,自然被捧在手心了怀挠。
Kotlin在編寫(xiě)代碼時(shí)有如下優(yōu)勢(shì):
- 代碼簡(jiǎn)潔高效
- Android Jetpack 與其他庫(kù)中的 Kotlin 支持
- 可與 Java 的一起使用
- 空指針安全
同樣,kotlin支持函數(shù)式編程害捕、支持lambda表達(dá)式绿淋、流式API...,此處略去一萬(wàn)字尝盼」總之,Java是上世紀(jì)的編程語(yǔ)言了东涡,當(dāng)你學(xué)過(guò)了Kotlin之后 ,你會(huì)發(fā)現(xiàn)倘待,之前寫(xiě)過(guò)那么多的代碼都是在浪費(fèi)生命疮跑。
語(yǔ)法基礎(chǔ)
變量
var age: Int = 0
var num = 20
val TYPE: Int = 100
var name: String = ""
寫(xiě)法形如:var str: String
用var或val聲明,前面是參數(shù)名凸舵,后面是參數(shù)的類型祖娘,中間用:分割
var即英文“variable”的意思,聲明成一個(gè)可變變量。val即英文“value”的意思渐苏,申明的是一個(gè)不可變變量掀潮,對(duì)應(yīng)的java里面的final。
kotlin中有優(yōu)秀的類推導(dǎo)機(jī)制琼富,age聲明了變量類型仪吧,num沒(méi)有聲明變量類型,但是依然可以通過(guò)鞠眉,是由Kotlin自動(dòng)推導(dǎo)了類型薯鼠。
常量
const
如果只讀屬性的值在編譯期是已知的,那么可以使用 const 修飾符將其標(biāo)記為編譯期常量械蹋,相當(dāng)于java中的public static final修飾出皇。
//const val 需與 companion object 搭配使用
companion object {
const val VERSION = 1
}
空指針檢查
在Kotlin里,可以用“?”表示可以為空哗戈,也可以用“!!”表示不可以為空郊艘。
給變量加上?標(biāo)識(shí),會(huì)通告所有使用該變量的地方唯咬,必須給出為空的補(bǔ)救措施纱注。
var info: String? = null
println(info?.length) //第一種補(bǔ)救:如果info為null,就不執(zhí)行后面的.length代碼
println(info!!.length) //第二種補(bǔ)救:這里如果為null副渴,我自己負(fù)責(zé)info奈附,會(huì)報(bào)出空指針,這種處理需慎用
if (info != null) { //第三種補(bǔ)救措施煮剧,如下這種同java寫(xiě)法
println(info.length)
}
println(info?.length ?: "空數(shù)據(jù)") //第四種補(bǔ)救措施斥滤,如果真的為null,則改為返回"空數(shù)據(jù)"
第二個(gè)檢查方式是let函數(shù)勉盅,?.當(dāng)然能解決大部分問(wèn)題佑颇,但是user每使用一次?.相當(dāng)于都加上了代碼if(user != null),實(shí)際上在同一個(gè)函數(shù)中草娜,我們只需要做一次非空判斷就行了挑胸,這就是我們的let函數(shù)的作用,在代碼塊內(nèi)容用it代替該對(duì)象宰闰。
user?.let {
it.login()
it.logout()
}
可見(jiàn)性修飾符
在 Kotlin 中有這四個(gè)可見(jiàn)性修飾符:private茬贵、 protected、 internal 和 public移袍。 如果沒(méi)有顯式指定修飾符的話解藻,默認(rèn)可見(jiàn)性是 public。
internal:在本模塊內(nèi)可見(jiàn)
字符串拼接
將變量拼接在字符串之內(nèi)葡盗,用${}來(lái)包裹變量即可
fun StringFormat(title: String) = {
"這里是拼接字符串${title}"
}
字符串自動(dòng)換行:
val content = """
哈哈哈哈
呵呵呵呵
嘿嘿嘿嘿
""".trimIndent()
值比較螟左、賦值:
println(name1.equals(name2)) //同java的equals
println(name1 == name2) //同equals作用,比較值的相等,這里為true
println(name1 === name2) //比較地址是否相等胶背,即比較是否為同一個(gè)對(duì)象巷嚣,這里為false
val name1 = "lili"
val name2 = "lili"
println(name1 === name2) //這里返回ture,因?yàn)樽址诔A砍厥菑?fù)用一份的
將條件判斷的結(jié)果賦值:
val num1 = 100
val num2 = 101
val max = if (num1 > num2) num1 else num2
when
用when關(guān)鍵字代替java中的switch使用钳吟,when作為判斷廷粒,條件可為任意類型
fun whenUse(obj: Any, type: Int) {
when(obj) {
1 ->
""
in 2..5 ->
""
is String ->
""
else ->
""
}
}
val week = 5
val info = when(week) {
1 -> "星期一"
1 -> "星期二"
3 -> println()
4 -> 5
-1 -> TODO() //Nothing類型:表示未實(shí)現(xiàn)的功能,會(huì)拋出異常 public inline fun TODO(): Nothing = throw NotImplementedError()
else -> "" //返回String類型砸抛,info一定是String類型
}
//將when的返回值直接使用起來(lái)賦值:
val info2 = when(week) {
1 -> "星期一"
2 -> "星期二"
3 -> println()
4 -> true
in 2..5 ->
""
else -> { //必須要有else
//else 返回括號(hào)评雌,info2就可以是任意類型 Any
}
}
Any
java中所有類的父類是Object,而Kotlin中所有類的父類為Any直焙。
object
Kotlin中沒(méi)有大寫(xiě)的Object了景东,而是有小寫(xiě)的object,表示單例奔誓。
class SingleTon {
object Holder {
var instance = SingleTon()
}
}
is
Kotlin中用is代碼java中的instanceOf 來(lái)判斷類型
fun charge() {
var type = ""
if (type is String) { //instanceOf
}
}
in使用
- in來(lái)檢查一個(gè)值是否在一個(gè)區(qū)間內(nèi)
fun isLetter(c: Char) = c in 'a'..'z' || c in 'A'..'Z' //檢查字符是否為字母
- 檢查list中是否有元素name
fun inList() {
val names = listOf("lala", "haha", "yaya")
if("lili" in names) {
}
}
takeIf斤吐,takeUnless函數(shù)
user.name.takeIf {
TextUtils.isEmpty(it)
}.let {
print(it)
}
user.name.takeUnless {
!TextUtils.isEmpty(it)
}.let {
print(it)
}
takeIf的閉包返回一個(gè)判斷結(jié)果,如果為false時(shí)takeIf函數(shù)返回null厨喂;takeUnless與takeIf相反和措,為true時(shí)takeUnless函數(shù)返回null。
使用場(chǎng)景:只需要單個(gè)if分支語(yǔ)句的時(shí)候
優(yōu)點(diǎn):
可以配合其他作用域函數(shù)返回的結(jié)果蜕煌,做出單向判斷派阱,保持鏈?zhǔn)秸{(diào)用
簡(jiǎn)化寫(xiě)法,邏輯清晰斜纪,減少代碼量贫母,代碼更優(yōu)雅
init
主構(gòu)造函數(shù)里不能寫(xiě)代碼,那我們?cè)趺闯跏蓟@個(gè)類的代碼呢盒刚?Kotlin則是提供了初始化模塊腺劣,基本上就是用init修飾符修飾一個(gè){},在類初始化時(shí)執(zhí)行這段代碼因块。
class User() {
init {
}
}
數(shù)組
創(chuàng)建數(shù)組
var names = arrayOf("小王", "小花", "小紅", "小明")
var ages = arrayOf(22, 19, 18)
var array = arrayOfNulls<String>(5) //創(chuàng)建空數(shù)組
array.set(1, "哈哈") //賦值
數(shù)組遍歷
fun arrayForEach() {
var names = arrayOf("小王", "小花", "小紅", "小明")
for (i in 1..5 step 1) { //跳過(guò)第1步遍歷
}
for (i in 0..names.size-1) { //遍歷names數(shù)組橘原,相當(dāng)于java里 for (int i=0:i<names.length;i++)
}
for (i in names.indices) { //正序遍歷
}
for (i in 1 until 10){ //為一個(gè)左閉右開(kāi)的區(qū)間,打印1到9
print("$i")
}
for (i in names.size downTo 0) { //倒序遍歷
}
for (i in names.reversed()) { //反轉(zhuǎn)遍歷數(shù)組
}
repeat(10) { //打印0到9
print(it)
}
names.forEach { //forEach函數(shù)遍歷涡上,Kotlin里的集合都自帶foreach函數(shù)
}
names.forEachIndexed { index, s -> //帶position的遍歷
}
}
in的用法:val num = 1..10趾断,表示變量num是一個(gè)[1,10]的區(qū)間。
step表步長(zhǎng)吩愧,表示遍歷間隔的個(gè)數(shù)芋酌。
類與對(duì)象
類構(gòu)造函數(shù)
在 Kotlin 中的一個(gè)類可以有一個(gè)主構(gòu)造函數(shù)以及一個(gè)或多個(gè)次構(gòu)造函數(shù)。如果主構(gòu)造函數(shù)沒(méi)有任何注解或者可見(jiàn)性修飾符耻警,可以省略這個(gè) constructor 關(guān)鍵字。
class User constructor(name: String, age: Int, id: Int)
class User(name: String, age: Int, id: Int)
操作示例:
class User(val name: String) { //主構(gòu)造函數(shù)
var age = 0
var sex = "man"
/**
* 有參次構(gòu)造方法
*/
constructor(name: String, age: Int, id: Int): this(name) { //次構(gòu)造函數(shù),必須去調(diào)用主構(gòu)造
}
constructor(name: String, sex: String): this(name) {
this.sex = sex
}
/**
* 無(wú)參次構(gòu)造方法
*/
constructor(): this("暫無(wú)名字") {
}
init {
println("name is $name")
println("age is $age")
}
fun main() {
User() //次構(gòu)造
User("小明") //主構(gòu)造
User("小明", 18, 1) //次構(gòu)造
}
}
創(chuàng)建對(duì)象
Kotlin中創(chuàng)建對(duì)象不再需要關(guān)鍵字new了甘穿,直接創(chuàng)建對(duì)象:
var user = User()
繼承
在Kotlin中的類和方法默認(rèn)是不可以被繼承的腮恩,需要加上關(guān)鍵字open,才允許被繼承温兼。繼承用":"表示秸滴,類和接口的繼承都用":",類需要加()募判。
class VipUser : User(), Use {}
open class User {}
interface Use {}
內(nèi)部類
class OuterClass {
val outerInfo: String = "outerInfo"
fun show() {
InnerClass().show() //外部類可以訪問(wèn)內(nèi)部類
}
//加上inner關(guān)鍵字才是內(nèi)部類荡含,內(nèi)部類才能和外部類相互訪問(wèn),否則不是內(nèi)部類而是嵌套類
inner class InnerClass {
fun show() = println("my outerclass info: " + outerInfo) //內(nèi)部類也可以訪問(wèn)外部類
}
}
數(shù)據(jù)類
即為我們通常使用的bean類届垫,一般在定義數(shù)據(jù)類的時(shí)候释液,我們需要手動(dòng)實(shí)現(xiàn)這個(gè)類的equals()、hashcode()装处、toString()等方法误债,這些方法是必要的。但是加上data關(guān)鍵字后妄迁,該類會(huì)自動(dòng)生成這些方法寝蹈。
data class User(var name: String) {
}
單例類
在java中,單例的寫(xiě)法需要自己手動(dòng)實(shí)現(xiàn)登淘,比如用懶漢式還是餓漢式箫老。但是在Kotlin中單例就非常簡(jiǎn)化了,只需要將class替換成object就可以了黔州。
object SingleTonClass {
var params: String? = null
fun function() {
}
}
SingleTonClass.function()
SingleTonClass.params
使用單例時(shí)不需要再獲取instance實(shí)例對(duì)象了耍鬓,Kotlin已經(jīng)在內(nèi)部幫我們創(chuàng)建了一個(gè)單例的實(shí)例,直接使用類名調(diào)用它的屬性和方法辩撑。
compaion object(伴生對(duì)象)
在java中static表靜態(tài)變量界斜,kotlin中取消了static,用compaion object來(lái)代替它的用法合冀。
class Companionobject {
//伴生對(duì)象的由來(lái)各薇,是kotlin沒(méi)有static靜態(tài)
//不管對(duì)象創(chuàng)建多少個(gè),companion object只會(huì)初始化一次
companion object {
val info = "lili" //靜態(tài)變量
fun showInfo() {
println(info)
}
}
fun test() {
//背后代碼:生成了 Companionobject.companion類
println(info)
showInfo()
}
}
Kotlin 接口
//kotlin的接口
//接口里面的所有成員和方法和接口本身都是 public open的
//實(shí)現(xiàn)類不僅要重寫(xiě)接口的函數(shù)君躺,還要重寫(xiě)接口的成員
interface IUSB {
var usbVersionInfo: String
fun insertUSB(): String
}
//成員重寫(xiě)寫(xiě)到實(shí)現(xiàn)類的構(gòu)造方法中 方式
class Mouse(override var usbVersionInfo: String = "USB 3.0") : IUSB {
override fun insertUSB(): String = usbVersionInfo
}
//成員重寫(xiě)寫(xiě)到實(shí)現(xiàn)類中的方式
class KeyBorad: IUSB {
override var usbVersionInfo: String = "USB 3.0"
get() { //get/set 方法會(huì)調(diào)用代碼塊內(nèi)容峭判,并返回一個(gè)值
println("獲取了 usbVersionInfo ${usbVersionInfo}")
return field
}
set(value) {
println("設(shè)置了 usbVersionInfo ${usbVersionInfo}")
field = value //field表示該 usbVersionInfo 屬性
}
override fun insertUSB(): String = "keyboard $usbVersionInfo"
}
fun showInterface() {
val usb1 = Mouse()
usb1.insertUSB()
val usb2 = KeyBorad()
usb2.usbVersionInfo = "sfd" //使用屬性調(diào)用get() 賦值屬性調(diào)用set()
usb2.insertUSB()
}
JvmField JvmStatic
// JvmStatic JvmField注解使用
//用于 Java調(diào)用kotlin 屬性和方法 兼容性 而產(chǎn)生的
//JvmField注解使用 將默認(rèn)的private修改成了public訪問(wèn)符
//JvmStatic 將 companion object 中的 屬性、方法能像kotlin一樣能直接調(diào)用
class Personz {
@JvmField
val names = listOf("lala", "haha", "yaya")
//這個(gè) names轉(zhuǎn)成java就是如下代碼棕叫, val修飾的 names屬性成了 private final 林螃,外部調(diào)用不到,而提供了一個(gè)方法獲取
// 使用了 @JvmField注解俺泣,將該屬性從private變成public疗认,能直接獲取val修飾的屬性
/**
* public final class Personz {
* @NotNull
* private final List names = CollectionsKt.listOf(new String[]{"lala", "haha", "yaya"});
*
* @NotNull
* public final List getNames() {
* return this.names;
* }
* }
*/
companion object {
@JvmField
val name : String = "lala"
@JvmStatic
fun printlnPersonName() = println("名字: " + name)
}
}
//java中調(diào)用代碼
public void getJvmFieldStatic() {
// new Personz().getNames(); //沒(méi)加 jvmField的調(diào)用
List<String> names = new Personz().names; //加了 jvmField的調(diào)用
//沒(méi)加 JvmStatic完残,java調(diào)用 Companion object需要加一層 Companion類來(lái)調(diào)用
// Personz.Companion.getName();
// Personz.Companion.printlnPersonName();
String name = Personz.name;
//加上 JvmStatic,就有了和Kotlin一樣的調(diào)用效果横漏,把函數(shù)寫(xiě)到了 Companion類 外面
Personz.printlnPersonName();
}
range符
fun range(number: Int) {
//range 范圍
if (number in 10..59) {
println("不及格")
} else if (number in 0 .. 9) {
println("很差")
} else if (number in 60..100) {
println("及格")
} else {
println("分?jǐn)?shù)不合法")
}
}
將現(xiàn)有Java文件轉(zhuǎn)換為Kotlin文件
右鍵->Convert Java File to Kotlin File
我們可以將現(xiàn)有Java文件代碼轉(zhuǎn)換為Kotlin文件代碼谨设,而實(shí)現(xiàn)從java往kotlin上轉(zhuǎn)移《薪剑可以針對(duì)單個(gè)文件扎拣,某個(gè)包,某個(gè)module素跺,或者整個(gè)項(xiàng)目轉(zhuǎn)為kotlin文件二蓝。
轉(zhuǎn)換完成之后可能會(huì)有少量語(yǔ)法錯(cuò)誤,需要手動(dòng)修改一下指厌。
將kotlin代碼轉(zhuǎn)為java
在Tools->Kotlin->show Kotlin ByteCode刊愚,點(diǎn)擊Decompile轉(zhuǎn)回類似Java代碼。
教程參考:
https://www.bilibili.com/video/BV1kT4y1o7nP