Kotlin入坑的理由:
1,大大減少Java樣板代碼;
2,避免空指針異常等;
3,充分利用JVM,Android現(xiàn)有的庫,和JAVA可以完全兼容;
4,Google推薦為Android開發(fā)的首選;
5,編譯器的良好支持。
我們現(xiàn)在在新功能或者新項目中使用kotlin語言,而舊的項目只需要簡單的維護的則繼續(xù)使用java語言。不管怎樣,你都可以嘗試使用kotlin去編程。
Kotlin的使用
函數(shù)
函數(shù)定義使用關(guān)鍵字 fun,參數(shù)格式為:參數(shù) : 類型
/**
* 計算兩個數(shù)的差,返回類型為Int
*/
fun testSub(a: Int, b: Int): Int {
return a - b
}
kotlin語言默認支持lambda表達式
// 使用lambda表達式
fun testLambda() {
val sumLambda: (Int, Int) -> Int = {x,y -> x+y}
println(sumLambda(1,2)) // 輸出 3
}
變長參數(shù)測試
fun testVararg(vararg v:Int){
for(vt in v){
print(vt)
}
}
fun testVar(){
testVararg(1,2,3)
}
此外,kotlin還提供了便捷的字符串模板
/**
* 字符串模板
*/
fun testTemp() {
val s = "kotlin"
val curStr = "我正在使用$s"
print(curStr)
}
打印出來是”我正在使用kotlin“,可以省去java的字符串拼接;
通過上面的例子發(fā)現(xiàn)kotlin省略了;號的結(jié)束符號,此外kotlin定義字段的時候,只有var,val兩個修飾符,var表示可變變量定義,val表示不可變變量定義和Java中的final修飾的變量類似,并且kotlin可以聲明變量的時候不指定變量類型,由編譯器自動推斷。
循環(huán)(for和while)
/**
* for循環(huán)
*/
fun testFor() {
for (i in 1..4) {
print(i)
}
for (i in 1..4 step 2) {
print(i)
}
//對集合進行迭代
val items = listOf("apple", "banana", "kiwi")
for(item in items){
println(item)
}
for(index in items.indices){
println("item at $index is ${items[index]}")
}
}
/**
* 使用兩種while遍歷
*/
fun testWhile() {
var x = 5
while (x > 0) {
println( x--)
}
var y = 5
do {
println(y--)
} while(y>0)
}
條件語句(if和when)
值得注意的是kotlin中并沒有swith語句,而使用了when替代;
/**
* when 將它的參數(shù)和所有的分支條件順序比較,直到某個分支滿足條件。
* 類似于switch
*/
fun testWhen(x: Int) {
when (x) {
is Int -> print(x.toString())
else -> {
print("不是int類型")
}
}
}
一個 if 語句包含一個布爾表達式和一條或多條語句。
kotlin中使用以下的表達式來替換java中的三元操作符:
// 作為表達式
val max = if (a > b) a else b
使用 in 運算符來檢測某個數(shù)字是否在指定區(qū)間內(nèi)
/**
* 使用區(qū)間
*/
fun testIf() {
val x = 5
if (x in 1..8) {
println("x 在區(qū)間內(nèi)")
}
}
Kotlin中的對象和類
Kotlin中的類都是繼承Any類(注意:并不是Object),它是所有類的超類,對于沒有超類型聲明的類是默認超類,Kotlin中與Java相同只能使用單繼承。constructor為構(gòu)造函數(shù),init函數(shù)里面可以進行初始化操作。
class Person constructor(firstName: String) {
init {
println("FirstName is $firstName")
}
}
下面來看看對象和類的使用
/**
* 接口里面的成員變量默認為open類型
* kotlin接口與java 8類似,可以在接口里面實現(xiàn)方法
*/
interface TestInterface {
fun test() {
print("TestInterface接口中的test()調(diào)用")
}
}
/**
* 類屬性修飾符
* abstract // 抽象類
* final // 類不可繼承,默認屬性
* enum // 枚舉類
* open // 類可繼承,類默認是final的
* annotation // 注解類
*
* 訪問權(quán)限修飾符
* private // 僅在同一個文件中可見
* public // 所有調(diào)用的地方都可見
* internal // 同一個模塊中可見
* 沒有顯式定義訪問修飾符,默認為“public ”,對所有可見。
*/
open class Outer {
private val bar: Int = 1
private val name: String = "123456"
class Nested {
fun foo(): String {
val curString: String = "123"
return curString
}
}
inner class Inner {
fun innerTest(): String {
return this@Outer.name
}
}
open fun setInterface(test: TestInterface) {
test.test()
}
open fun test() {
}
}
/**
* Kotlin也只能單繼承
*/
class OuterImpl : Outer(), TestInterface {
/**
* 如果繼承和實現(xiàn)的方法中,有同樣的test方式,需要這樣調(diào)用父類或者接口的方法
* 另外:屬性重寫使用 override 關(guān)鍵字,屬性必須具有兼容類型,每一個聲明的屬性都可以通過初始化程序或者getter方法被重寫
* 你可以用一個var屬性重寫一個val屬性,但是反過來不行。因為val屬性本身定義了getter方法,重寫為var屬性會在衍生類中額外聲明一個setter方法
*/
override fun test() {
super<Outer>.test()
super<TestInterface>.test()
}
override fun setInterface(test: TestInterface) {
super.setInterface(test)
}
}
/**
* 測試
*/
fun testOuter(): String {
var curStr = Outer.Nested().foo()// 調(diào)用格式:外部類.嵌套類.嵌套類方法/屬性
val curName = Outer().Inner().innerTest()//調(diào)用內(nèi)部類
//匿名內(nèi)部類
Outer().setInterface(object : TestInterface {
override fun test() {
print("這里是一個匿名內(nèi)部類")
}
})
OuterImpl().setInterface(object : TestInterface {
override fun test() {
print("這里使用了繼承實現(xiàn)匿名內(nèi)部類")
}
})
return "Outer.Nested().foo()中的curString=${Outer.Nested().foo()} Outer().Inner().innerTest()中name=$curName"
}
var curStr = Outer.Nested().foo()// 調(diào)用格式:外部類.嵌套類.嵌套類方法/屬性
val curName = Outer().Inner().innerTest()//調(diào)用內(nèi)部類
值得注意的是以上調(diào)用方式的不同。
Kotlin接口
Kotlin 接口與 Java 8 類似,使用 interface 關(guān)鍵字定義接口,允許方法有默認實現(xiàn):
interface CustomInterface {
fun testOne() // 未實現(xiàn)
fun testTwo() { //已實現(xiàn)
// 可選的方法體
println("testTwo()")
}
}
接口中的屬性只能是抽象的,不允許初始化值,接口不會保存屬性值,實現(xiàn)接口時,必須重寫屬性
interface CustomInterface{
var name:String //name 屬性, 抽象的
}
class CustomImpl:CustomInterface{
override var name: String = "Hubery" //重載屬性
}
伴生對象
類內(nèi)部的對象聲明可以用 companion 關(guān)鍵字標記,這樣它就與外部類關(guān)聯(lián)在一起,我們就可以直接通過外部類訪問到對象的內(nèi)部元素。另外一個類里面只能夠有一個伴生對象,也就是只能有一個companion修飾符。
java中我們常常使用static,而kotlin里面沒有這個而使用伴生對象替代。不過并不完全相等。
companion object {
val EXTRA_ORDER_ID = "order_id"
}
類似于Java中如下定義 :
public static final String EXTRA_ORDER_ID = "extra_order_id";
需要注意的是:在kotlin語言中別的類需要訪問就直接 類名.字段名訪問就行,而在java中調(diào)用kotlin的EXTRA_ORDER_ID,在這里就需要使用
類名.Companion.getEXTRA_ORDER_ID()
伴生對象的成員看起來像其他語言的靜態(tài)成員,但在運行時他們?nèi)匀皇钦鎸崒ο蟮膶嵗蓡T。比如還可以實現(xiàn)接口:
interface CustomInterface<T> {
fun create(): T
}
class DemoClass {
companion object : CustomInterface<DemoClass > {
override fun create(): DemoClass = DemoClass ()
}
}
數(shù)據(jù)類和密封類
數(shù)據(jù)類
kotlin使用data來修飾的只包含數(shù)據(jù)的類。
json字符串{"key":123,"title":"This is title"}
使用kotlin實現(xiàn):
data class Bean(
@SerializedName("key") val key: Int,
@SerializedName("title") val title: String
)
使用java顯示
public class Bean{
@SerializedName("key")
private int key;
@SerializedName("title")
private String title;
public int getKey() {
return key;
}
public void setKey(int key) {
this.key = key;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
看上面兩段代碼,發(fā)現(xiàn)kotlin的代碼會簡潔很多,特別是字段非常多的時候。
密封類
密封類用來表示受限的類繼承結(jié)構(gòu):當一個值為有限幾種的類型, 而不能有任何其他類型時。在某種意義上,他們是枚舉類的擴展:枚舉類型的值集合 也是受限的,但每個枚舉常量只存在一個實例,而密封類 的一個子類可以有可包含狀態(tài)的多個實例。
聲明一個密封類,使用 sealed 修飾類,密封類可以有子類,但是所有的子類都必須要內(nèi)嵌在密封類中。 此外,sealed 不能修飾 interface ,abstract class(會報 warning,但是不會出現(xiàn)編譯錯誤)
sealed class SealedDemo
data class Const(val number: Double) : SealedDemo()
data class Sum(val s1: SealedDemo, val s2: SealedDemo) : SealedDemo()
object NotANumber : SealedDemo()
fun eval(SealedDemo: SealedDemo): Double = when (SealedDemo) {
is Const -> SealedDemo.number
is Sum -> eval(SealedDemo.s1) + eval(SealedDemo.s2)
NotANumber -> Double.NaN
}
密封類的關(guān)鍵好處在于使用 when 表達式 的時候,如果能夠 驗證語句覆蓋了所有情況,就不需要為該語句再添加一個 else 子句了。