Android Kotlin開發(fā)實(shí)例及語(yǔ)法詳解
我們提供的服務(wù)有:成都網(wǎng)站設(shè)計(jì)、成都網(wǎng)站制作、外貿(mào)網(wǎng)站建設(shè)、微信公眾號(hào)開發(fā)、網(wǎng)站優(yōu)化、網(wǎng)站認(rèn)證、留壩ssl等。為上千家企事業(yè)單位解決了網(wǎng)站和推廣的問題。提供周到的售前咨詢和貼心的售后服務(wù),是有科學(xué)管理、有技術(shù)的留壩網(wǎng)站制作公司
前言
Kotlin是一種在 Java虛擬機(jī)上執(zhí)行的靜態(tài)型別編程語(yǔ)言,它主要是由俄羅斯圣彼得堡的JetBrains開發(fā)團(tuán)隊(duì)所發(fā)展出來的編程語(yǔ)言。該語(yǔ)言有幾個(gè)優(yōu)勢(shì)
1. 簡(jiǎn)潔
它大大減少你需要寫的樣板代碼的數(shù)量。
2. 安全
避免空指針異常等整個(gè)類的錯(cuò)誤。
3. 通用
構(gòu)建服務(wù)器端程序、Android 應(yīng)用程序或者在瀏覽器中運(yùn)行的前端程序。
4. 互操作性
通過 100% Java 互操作性,利用 JVM 既有框架和庫(kù)。
配置
在我們的AndroidStudio開發(fā)工具中,要想使用Kotlin這個(gè)優(yōu)秀的開發(fā)語(yǔ)言,我們需要安裝插件,直接在安裝插件界面搜索Kotlin然后安裝。之后再gradle文件增加如下配置
apply plugin:'kotlin-android' apply plugin:'kotlin-android-extensions' dependencies { compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" }
項(xiàng)目gradle文件
buildscript { ext.kotlin_version = '1.1.1' repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:2.3.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } }
完成上面的配置后,我們就可以愉快的玩耍了。
Kotlin示例
首先我們還和以前一樣,創(chuàng)建一個(gè)Android項(xiàng)目,自動(dòng)創(chuàng)建一個(gè)Activity之后我們?cè)賱?chuàng)建一個(gè)java類
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG) .setAction("Action", null).show(); } }); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.menu_main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); //noinspection SimplifiableIfStatement if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); } } public class Test { private static String str = null; public static void main(String[] args) { str = "Code4Android"; System.out.println(str); } }
那上面的代碼如果用kotlin實(shí)現(xiàn)是什么樣子呢。盡管現(xiàn)在我們還不能寫出Kotlin代碼,但是在安裝插件后AS中提供了自動(dòng)轉(zhuǎn)換Kotlin代碼的功能
轉(zhuǎn)換后的Kotlin代碼
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val toolbar = findViewById(R.id.toolbar) as Toolbar setSupportActionBar(toolbar) val fab = findViewById(R.id.fab) as FloatingActionButton fab.setOnClickListener { view -> Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG) .setAction("Action", null).show() } } override fun onCreateOptionsMenu(menu: Menu): Boolean { // Inflate the menu; this adds items to the action bar if it is present. menuInflater.inflate(R.menu.menu_main, menu) return true } override fun onOptionsItemSelected(item: MenuItem): Boolean { val id = item.itemId if (id == R.id.action_settings) { return true } return super.onOptionsItemSelected(item) } } object Test { private var str: String? = null @JvmStatic fun main(args: Array) { str = "Code4Android" println(str) } }
注意:AS提供的java代碼自動(dòng)轉(zhuǎn)換功能,我們不要輕易使用,更不要轉(zhuǎn)化我們成熟的項(xiàng)目,如果需要就需要我們自己去重構(gòu)實(shí)現(xiàn)。否則會(huì)有意向不到的事情等著你,畢竟轉(zhuǎn)換不是那么智能。上面的代碼只是讓你先簡(jiǎn)單熟悉下Kotlin代碼時(shí)什么樣子的,接下來我們先去學(xué)習(xí)一下Kotlin的基本語(yǔ)法。相信很容易上手。
Hello World!
我們由一個(gè)簡(jiǎn)單的”Hello World!”輸出程序開始。與新建java文件類似,如下圖,我們選擇Kotlin File/Class.創(chuàng)建一個(gè)Kotlin文件。
package com.learnrecord /** *Created by Code4Android on 2017/4/21. */ var str: String = "" fun main(args: Array) { str = "Hello World!" println(str) }
上述代碼就是簡(jiǎn)單的輸出一個(gè)字符串“Hello World”,package 后面跟的是包名,我們看出了和java文件的區(qū)別,在包名后面沒有以分號(hào)“;”結(jié)尾。在Kotlin語(yǔ)法中,語(yǔ)句結(jié)尾都不在有分號(hào)“;”。
在Kotlin中變量聲明有兩種類型,val修飾變量是只讀變量即只能賦值一次,再次賦值時(shí)就會(huì)編譯錯(cuò)誤
,如果我們需要多次修改值就需要使用var。在上面的 var str: String = “”中,str是變量名,:String,表明該變量是String類型變量,后面就是賦值語(yǔ)句。我們也可以這樣寫var str= “”省略了生命變量類型,它可以根據(jù)賦的值而自動(dòng)推斷出類型。如果我們使用下面賦值語(yǔ)句str=null,發(fā)現(xiàn)null是不能賦值的,這就是Kotlin的特性,如果我們想賦值null,可以修改為 var str:String?=”“。
fun就是函數(shù)生命,而這個(gè)main函數(shù)就和我們java中的main方法一樣,是程序執(zhí)行的入口。println就是一個(gè)打印輸出。
Kotlin聲明類型
在Kotlin中有如下幾種Number類型,他們都是繼承自Number抽象類。
Float(32位),Double(64),Int(32),Byte(8),Short(16),Long(64,類型用大寫L,如12L),Any(任意類型),數(shù)組類型Array 根據(jù)傳入的泛型數(shù)據(jù)自動(dòng)匹配類型,Kotlin還提供了指定類型的Array,如ByteArray,CharArray,ShortArray,IntArray,LongArray,F(xiàn)loatArray,DoubleArray,BooleanArray。在數(shù)組類型中都提供了get(index),set(index,value)及iterator()方法供我們使用。
val iArray: IntArray = intArrayOf(1, 2, 3) val sArray: Array= Array (3, { i -> i.toString() }) val anyArray: Array = arrayOf(1, "2", 3.0, 4.1f) // 可將類型進(jìn)行混排放入同一個(gè)數(shù)組中 val lArray: LongArray = longArrayOf(1L, 2L, 3L)
函數(shù)
我們先來實(shí)現(xiàn)一個(gè)簡(jiǎn)單的數(shù)值求和的函數(shù),通用實(shí)現(xiàn)方法如下
fun sum(a: Int, b: Int): Int { return a + b }
傳入兩個(gè)Int型數(shù)值,sum是函數(shù)名,括號(hào)后面的:Int表示該函數(shù)返回Int的值,函數(shù)體中對(duì)兩個(gè)數(shù)字相加并返回。在Kotlin中表達(dá)式也可以作為函數(shù)體,編譯器可以推斷出返回類型,可以簡(jiǎn)化為
fun sum(a: Int, b: Int) = a + b
為了更好理解表達(dá)式可以作為函數(shù)體,我們可以創(chuàng)建一個(gè)函數(shù)獲取兩個(gè)數(shù)的最大值,如下:
fun max1(a:Int,b:Int)=if (a>b) a else b
需要注意的是若if后有多個(gè)表達(dá)式,如下
fun max1(a:Int,b:Int)= if (a > b) { println(a) a } else { println(b) //如果我們將println(b)放到b的下面,運(yùn)行會(huì)返回kotlin.Unit為無類型,返回值總是最后一個(gè)表達(dá)式的返回值或值 b } println(max1(1,3))
括號(hào)中的表達(dá)式順序決定了返回的值及其類型。
如果我們的方法體僅僅是打印字符串,并不返回值則
fun printInt(a: Int): Unit { println(a) }
Unit就類似我們java中的void,即沒有返回值,此時(shí)我們可以省略
fun printInt(a: Int) { println(a) }
對(duì)于函數(shù)體,方法或者類等和java一樣也有一些修飾符,如下
操作符
直接上代碼如下
//操作符 shl 下面對(duì)Int和Long var a: Int = 4 var shl: Int = a shl (1) //Java中的左移運(yùn)算符 << var shr: Int = a shr (1) //Java中的右移運(yùn)算符 >> var ushr: Int = a ushr (3) //無符號(hào)右移,高位補(bǔ)0 >>> var and: Int = 2 and (4) //按位與操作 & var or: Int = 2 or (4) //按位或操作 | var xor: Int = 2 xor (6) //按位異或操作 ^ print("\nshl:" + shl + "\nshr:" + shr + " \nushr:" + ushr + "\nand:" + and + "\nor:" + or + "\nxor:" + xor)
輸出信息為
shl:8 shr:2 ushr:0 and:0 or:6 xor:4
在上面的部分操作符可達(dá)到邏輯操作符, 當(dāng)我們使用Boolean時(shí),or() 相當(dāng)于 ||,and() 相當(dāng)于 &&, xor() 當(dāng)操作符兩邊相反時(shí)為true,否則為false ,not()時(shí)取反。
數(shù)組遍歷及控制語(yǔ)句
遍歷數(shù)組
fun forLoop(array: Array) { //第一種方式直接輸出字符(類似java foreach) for (str in array) { println(str) } //Array提供了forEach函數(shù) array.forEach { println(it) } //array.indices是數(shù)組索引 for (i in array.indices) { println(array[i]) } //(類似javafor(int i=0;i
使用when判斷類型
fun whenFun(obj: Any) { when (obj) { 25 -> println("25") "Kotlin" -> println("Kotlin") !is String -> println("Not String") is Long -> println("Number is Long") else -> println("Nothing") } }
is 和java中instanceof是一個(gè)作用判斷是否為某個(gè)類型。!is即判斷不是某個(gè)類型。
//@定義label,一般用在內(nèi)層循環(huán)中跳到外層循環(huán):i in 0..2等價(jià)于java中 for(int i=0;i<=2;i++)效果 loop@ for (i in 0..2) { for (j in 0..3) { println("i:" + i + " j:" + j) if (j == 2) //continue@loop//跳到外層循環(huán),繼續(xù)往下執(zhí)行 break@loop //跳到外層循環(huán)label處,跳出改層循環(huán) } }
倒序輸出是downTo
//倒序輸出5 4 3 2 1 0 for (i in 5 downTo 0) { println(i) } //設(shè)置輸出數(shù)據(jù)步長(zhǎng) for (i in 1..5 step 3) print(i) // 輸出 14 //step和downTo混合使用 for (i in 5 downTo 1 step 3) print(i) //輸出52
類與枚舉
/*** constructor:構(gòu)造函數(shù) * constructor無修飾符(如:private)時(shí),constructor可以省略: * 當(dāng)是無參構(gòu)造函數(shù)時(shí),整個(gè)構(gòu)造函數(shù)部分也可以省略,省略的構(gòu)造函數(shù)默認(rèn)是public的 * 由于primary constructor不能包含任何代碼,因此使用 init 代碼塊對(duì)其初始化, * 同時(shí)可以在初始化代碼塊中使用構(gòu)造函數(shù)的參數(shù) */ open class People private constructor(var id: String, var name: String) { //可以類中初始化屬性: var customName = name.toUpperCase() //初始化屬性 //次構(gòu)造函數(shù),使用constructor前綴聲明,且必須調(diào)用primary constructor,使用this關(guān)鍵字 constructor( id: String, name: String, age: Int) : this(id, name) { println("constructor") } init { println("初始化操作,可使用constructor參數(shù)") } //需要open修飾,子類才可以 open fun study() { print("study") } //People前的冒號(hào)":"是繼承的意思,實(shí)現(xiàn)類接口的時(shí)候也是冒號(hào) class Student(id: String, name: String) : People(id, name) { var test: Number = 3 private var name1: String? get() { return name1 } set(value) { name1 = value } //override修飾的方法,默認(rèn)是可以被繼承的。若希望不被繼承,可以使用 final 關(guān)鍵詞修飾 override fun study() { super.study() } } }
數(shù)據(jù)類用來保存數(shù)據(jù),類似于POJO類,使用data關(guān)鍵詞進(jìn)行定義,編譯器默認(rèn)會(huì)為數(shù)據(jù)類生成以下四個(gè)方法
通過數(shù)據(jù)類你會(huì)看到Kotlin的簡(jiǎn)潔性,我們創(chuàng)建一個(gè)Staff類,有String類型的name,position和泛型T(使用泛型僅僅是為了在Kotlin中接觸以下泛型)
java實(shí)現(xiàn)代碼:
public class StaffJ{ private String name; private String position; private T age; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPosition() { return position; } public void setPosition(String position) { this.position = position; } public T getAge() { return age; } public void setAge(T age) { this.age = age; } }
Kotlin數(shù)據(jù)類:
data class Staff(var name: String, val position: String,var age:T)
通過對(duì)比我們就看出了優(yōu)點(diǎn)了,一行代碼就實(shí)現(xiàn)了,具體使用
var staff = Staff("code4Android","Android工程師","22") //實(shí)例化對(duì)象
要獲取某個(gè)屬性如獲取名字staff.name,賦值就是staff.name=”code4Android2”,既然說了這樣可以賦值,但是動(dòng)手的小伙伴說為什么我敲的報(bào)錯(cuò)啊,如下
staff.position="前端"
編譯報(bào)錯(cuò)了,在前面我們說過val修飾的屬性只能賦值一次,那在這里val修飾的屬性我們是不能再次賦值的。
fun main(arg:Array){ var staff = Staff("code4Android","Android工程師","22") //實(shí)例化對(duì)象 staff.name="code4Android2" var staff1=staff.copy() //使用copy的時(shí)候可以指定默認(rèn)值,可以指定任意個(gè)用逗號(hào)","隔開 var staff2=staff.copy(name="ccc",position = "kotlin") println("name:${staff2.name} position:${staff2.position} age ${staff2.age}") //staff.position="Kotiln" //val不能再次賦值 var anotherStaff= Staff("code4Android","Android工程師",22) //實(shí)例化對(duì)象 println("staff toString(): ${staff.toString()} anotherStaff toString(): ${anotherStaff.toString()}") println("staff hashCode(): ${staff.hashCode()} anotherStaff hashCode(): ${anotherStaff.hashCode()}") println("staff is equals another staff ? ${staff.equals(anotherStaff)}") }
上面使用了字符模板,在Kotlin中有兩種字符模板形式,$<變量>、${<變量>}
var name:String="Code4Android" println("我的名字叫$name") println("我的名字叫${name}")
/** * java允許使用匿名內(nèi)部類;kotlin也有類似的概念,稱為對(duì)象表達(dá)式-object expressions */ open class KeyBord{ open fun onKeyEvent(code:Int):Unit = Unit } /**匿名內(nèi)部類**/ var key=object :KeyBord(){ override open fun onKeyEvent(code:Int):Unit = Unit }
枚舉
enum class Color{ RED,BLACK,BLUE,GREEN,WHITE } fun display(){ var color:Color=Color.BLACK Color.valueOf("BLACK") // 轉(zhuǎn)換指定name為枚舉值,若未匹配成功,會(huì)拋出IllegalArgumentException Color.values() //已數(shù)組的形式,返回枚舉值 println(color.name)////獲取枚舉名稱 println(color.ordinal)//獲取枚舉值在所有枚舉數(shù)組中定義的順序,0開始 }
在Kotlin中枚舉還支持方法。
擴(kuò)展
/** * fun receiverType.functionName(params){ *body *} * receiverType : 待擴(kuò)展的類名 * . :修飾符為擴(kuò)展符 * functionName :為自定義的擴(kuò)展函數(shù)名, * params :為自定義的擴(kuò)展函數(shù)參數(shù),可為空 * 擴(kuò)展函數(shù)作用域,受函數(shù)的visibility modifiers影響 * 擴(kuò)展函數(shù)并沒有對(duì)原類做修改,而是為被擴(kuò)展類的對(duì)象添加新的函數(shù)。 * 有一條規(guī)則,若擴(kuò)展函數(shù)和類原有函數(shù)一致,則使用該函數(shù)時(shí),會(huì)優(yōu)先使用類本身的函數(shù)。 */ class Employee(var name: String) { fun print() { println("Employee") } } //擴(kuò)展函數(shù) fun Employee.println() { print("println:Employee name is $name") } /** * 可以擴(kuò)展一個(gè)空對(duì)象 */ fun Any?.toString1(): String { if (this == null) return "toString1:null" else { return "toString1" + toString() } } /** * 擴(kuò)展屬性 * 由于擴(kuò)展屬性實(shí)際上不會(huì)向類添加新的成員, * 因此無法讓一個(gè)擴(kuò)展屬性擁有一個(gè)后端域變量. 所以,對(duì)于擴(kuò)展屬性不允許存在初始化器. * 擴(kuò)展屬性的行為只能通過明確給定的取值方法與設(shè)值方法來定義,也就意味著擴(kuò)展屬性只 * 能被聲明為val而不能被聲明為var.如果強(qiáng)制聲明為var,即使進(jìn)行了初始化, * 在運(yùn)行也會(huì)報(bào)異常錯(cuò)誤,提示該屬性沒有后端域變量。 */ val Employee.lastName: String get() { return "get:"+name }
使用
fun main(arg: Array) { var employee = Employee("Code4Android") employee.print() employee.println() println(employee.toString1()) println(employee.lastName) }
代理
** * 被代理接口 */ interface Base { fun display() } /** * 被代理類 */ open class BaseImpl : Base { override fun display() = print("just display me.") } /** * 代理類,使用:以及關(guān)鍵詞by進(jìn)行聲明 * 許代理屬性和其他繼承屬性共用 * class Drived(base: Base) : Base by base,BaseImpl() */ class Drived(base: Base) : Base by base //使用 fun show() { var b = BaseImpl() var drived = Drived(b) drived.display() } ** * 代理類型: * 懶加載:Lazy * 觀察者:Delegates.observable() * 非空屬性:Delegates.notNull<>() */ class Water { public var weight:Int by Delegates.notNull() /** * 代理屬性 */ public val name: String by lazy { println("Lazy.......") "Code4Android" } public var value: String by Delegates.observable("init value") { d, old, new -> println("$d-->$old->$new") } } fun main(arg: Array) { show() var water = Water() println(water.name) println(water.name) water.value = "11111" water.value = "22222" water.value = "33333" println(water.value) println(water.value) //必須先賦值否則IllegalStateException: Property weight should be initialized before get. water.weight=2 print(water.weight) }
操作符::
val String.lastChar: Char get() = this[this.length - 1] class A(val p: Int)
//1,反射得到運(yùn)行時(shí)的類引用: val c = Student::class //2,函數(shù)引用 fun isOdd(x: Int) = x % 2 != 0 val numbers = listOf(1, 2, 3) println(numbers.filter(::isOdd)) //3,屬性引用(在此引用main函數(shù)主體外聲明的變量) println(::x.get()) ::x.set(2) println(x) //4,::x 表達(dá)式評(píng)估為 KProperty類型的屬性,它允許我們使用 get() 讀它的值或者使用名字取回它的屬性 val prop = A::p println(prop.get(A(1))) //5,對(duì)于擴(kuò)展屬性 println(String::lastChar.get("abc")) //6,與 java 反射調(diào)用 println(A::p.javaClass), var f: Array = A::p.javaClass.declaredFields
伴生對(duì)象
伴生對(duì)象(companion object )類似于java中的靜態(tài)關(guān)鍵字static。在Kotlin沒有這個(gè)關(guān)鍵字,而是伴生對(duì)象,具體用法
open class People constructor(var id: String, var name: String){ //可以類中初始化屬性: var customName = name.toUpperCase() //初始化屬性 //使用constructor前綴聲明,且必須調(diào)用primary constructor,使用this關(guān)鍵字 constructor( id: String, name: String, age: Int) : this(id, name) { println("constructor") } init { println("初始化操作,可使用constructor參數(shù)") } //,Kotlin的class并不支持static變量,所以需要使用companion object來聲明static變量, // 其實(shí)這個(gè)platformStatic變量也不是真正的static變量,而是一個(gè)伴生對(duì)象, companion object { val ID = 1 } }
使用的話直接People.ID。
單例模式
在Kotlin中使用object修飾類的時(shí)候,。該類是單例對(duì)象。
/** * 使用object定義類,該類的實(shí)例即為單例,訪問單例直接使用類名,不能通過構(gòu)造函數(shù)進(jìn)行訪問,不允許有構(gòu)造函數(shù) * Place.doSomething() // 訪問單例對(duì)象 */ object Singleton { fun doSomething() { println("doSomething") } } /** * 實(shí)例化的時(shí)候,單例是懶加載,當(dāng)使用的時(shí)候才去加載;而對(duì)象表達(dá)式是在初始化的地方去加載。 * * 當(dāng)在類內(nèi)部使用 object 關(guān)鍵詞定義對(duì)象時(shí),允許直接通過外部類的類名訪問內(nèi)部對(duì)象進(jìn)而訪問其相關(guān)屬性和方法,相當(dāng)于靜態(tài)變量 * 可以使用companion修飾單例,則訪問其屬性或方法時(shí),允許省略單例名 * MyClass.doSomething() // 訪問內(nèi)部單例對(duì)象方法 */ class MyClass { companion object Singleton { fun doSomething() { println("doSomething") } } }
感謝閱讀,希望能幫助到大家,謝謝大家對(duì)本站的支持!
源碼下載:http://xiazai.jb51.net/201705/yuanma/KotlinForAndroid(jb51.net).rar