- Kotlin為什么可以編寫Android程序滥壕?
因為使用Java語言編寫的代碼最終需要編譯成.class文件才能執(zhí)行,而使用Kotlin編寫的代碼最終也會編譯成.class文件。對于Android操作系統(tǒng)而言,它無須關(guān)心也無法關(guān)心程序是用什么語言開發(fā)的,只要最終執(zhí)行的是一些合法的.class文件即可。 - 我們?yōu)槭裁匆褂肒otlin開發(fā)Android程序捍岳?
1、每行代碼行尾毫無意義的分號睬隶。
2锣夹、switch語句只支持int條件(java1.8版本后也支持String了),且case最后要加break苏潜。
3银萍、不是全面向?qū)ο笳Z言,而是半面向?qū)ο笳Z言恤左。
4贴唇、不支持字符串內(nèi)嵌表達(dá)式,拼接字符串繁雜飞袋。 - Kotlin學(xué)習(xí)資料
Kotlin-in-chinese
Kotlin極簡教程
Kotlin系列之let戳气、with、run巧鸭、apply瓶您、also函數(shù)的使用
kotlin學(xué)習(xí)筆記:object關(guān)鍵字介紹與java中的靜態(tài)變量與靜態(tài)方法的實現(xiàn)以及@JvmField和@JvmStatic的使用
Kotlin學(xué)習(xí)系列之:可變參數(shù)
AndroidStudio配置Gradle
- 父 build.gradle :
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
//創(chuàng)建變量存儲當(dāng)前Kotlin版本,方便幾個不同地方用到該版本號
ext.kotlin_version = '1.2.50'
//創(chuàng)建變量存儲Anko庫版本號纲仍,Anko是一個用來簡化一些Android任務(wù)的很強大的Kotlin庫
ext.anko_version='0.10.7'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.4'
//添加Kotlin構(gòu)建插件
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
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
- 子build.gradle:
apply plugin: 'com.android.application'
//Kotlin插件
apply plugin: 'kotlin-android'
//Kotlin Android Extensions plugin 插件
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 28
defaultConfig {
applicationId "cn.lq.com.kotlinstudy"
minSdkVersion 15
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
//添加Kotlin標(biāo)準(zhǔn)庫依賴
implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
//添加Anko庫依賴呀袱,anko-common是我們需要Anko庫的一小部分,以至于我們不會把沒用到的部分加進(jìn)來郑叠。
implementation"org.jetbrains.anko:anko-common:$anko_version"
}
-
快速配置方法夜赵,創(chuàng)建項目時直接勾選Include Kotlin support創(chuàng)建Kotlin項目
Kotlin基礎(chǔ)知識
1. 方法和變量
方法
package com.lg.www.kotlinstudy
//Kotlin中極度弱化了靜態(tài)的概念,盡量少用
// 定義靜態(tài)方法的方式一,定義頂層方法(不屬于任何一個類的方法)
fun staticMethod1(){
}
class KotlinTest {
//定義方法用fun乡革,返回值通過“:”接后面,無返回值不需要寫
/*四種限定符:
* public:默認(rèn)限定符寇僧,可以省略不寫
* private
* protected
* internal:同模塊限定符摊腋;同一module可以調(diào)用被internal修飾的方法,跨module不行婉宰。
*/
private fun helloworld(arg: String): String {
println(arg)
return arg
}
fun helloworld(): String{
return "hellow word"
}
//只有一行代碼的方法體可以進(jìn)行簡化歌豺,用等號連接返回值
fun helloworld() = "hellow word"
/*companion object:
我們需要一個類里面有一些靜態(tài)的屬性、常量或者函數(shù)心包,
從而方便我們不創(chuàng)建對象就可使用,我們可以使用 companion object馒铃。
這個對象被這個類的所有對象所共享蟹腾,對象里的屬性或者方法就像Java中的靜態(tài)屬性或者方法,
但是并不是靜態(tài)屬性或方法区宇,其實只是創(chuàng)建Companion靜態(tài)對象來調(diào)用里面的屬性和方法娃殖。
*/
companion object {
//伴生方法,類似靜態(tài)方法
fun companionMethod(){}
//定義靜態(tài)方法的方式二议谷,加靜態(tài)注解
@JvmStatic
fun staticMethod2(){}
}
}
變量
package com.lg.www.kotlinstudy
class KotlinTest {
//Any類型 (與java中的 Object 類似)
fun test(arg: Any) {
/*數(shù)據(jù)類型:
在Kotlin中炉爆,一切都是對象。沒有像Java中那樣的原始基本類型卧晓。
這個是非常有幫助的芬首,因為我們可以使用一致的方式來處理所有的
可用的類型。*/
val num: Int = 8 // 聲明并立即賦值
/*變量:
變量可以很簡單地聲明成可變( var修飾 )和不可變( val修飾 )的變量逼裆。
可以簡單的理解為var是可寫的郁稍,在它生命周期中可以被多次賦值;
而val是只讀的胜宇,僅能一次賦值耀怜,后面就不能被重新賦值。這個與Java中使
用的 final 很相似桐愉。但是不可變在Kotlin(和其它很多現(xiàn)代語言)中是一個很重要
的概念财破。
一個不可變對象意味著它在實例化之后就不能再去改變它的狀態(tài)了。如果你需要一
個這個對象修改之后的版本从诲,那就會再創(chuàng)建一個新的對象左痢。這個讓編程更加具有健
壯性和預(yù)估性。在Java中盏求,大部分的對象是可變的抖锥,那就意味著任何可以訪問它這
個對象的代碼都可以去修改它,從而影響整個程序的其它地方碎罚。
不可變對象也可以說是線程安全的磅废,因為它們無法去改變,也不需要去定義訪問控
制荆烈,因為所有線程訪問到的對象都是同一個拯勉。
所以在Kotlin中竟趾,只要可能,盡量在Kotlin中首選使用val不變值宫峦。因為事實上在程序
中大部分地方使用不可變的變量岔帽,可帶來很多益處,如:可預(yù)測的行為和線程安全导绷。
*/
// 變量如果沒有初始值類型不能省略
var varTest: String
val valTest: Int
valTest = 10// 聲明后賦值
//變量可以不需要聲明類型犀勒,光標(biāo)停留在變量上時按Ctrl+Q可以查看變量類型
var a = 1 // 自動推斷出 `Int` 類型
a = 2
println(a)
val b = "b"
//b = "c" //編譯器會報錯: Val cannot be reassigned
println(b)
//低精度類型無法自動轉(zhuǎn)換成高精度類型,如Int無法自動轉(zhuǎn)換成Long
val x: Int = 10
//val y:Long = x // Type mismatch
val y: Long = x.toLong()//需要顯式地調(diào)用對應(yīng)的類型轉(zhuǎn)換函數(shù)進(jìn)行轉(zhuǎn)換
//is 運算符檢測一個表達(dá)式是否某類型的一個實例
if (arg is String) {
// `result` 在該條件分支內(nèi)自動轉(zhuǎn)換成 `String`
// 檢測后的分支中可以直接當(dāng)作該類型使用妥曲,無需顯式轉(zhuǎn)換
println(arg::class) //class kotlin.String
val length = arg.length
arg.length
println(length)
}
/*原始字符串(raw string):
由三重引號(""")分隔,原始字符串可以包含換行符和任何其他字符贾费,
都會原樣輸出,不會進(jìn)行轉(zhuǎn)義*/
val rawString = """\a/b\\c//d"""
println(rawString)// \a/b\\c//d
//字符串模板表達(dá)式:以美元符號($)連接變量檐盟,方便字符串拼接
val s = "Hello"
val templateString = "$s has ${s.length} characters"
println(templateString) //Hello has 5 characters
}
}
2. 類和繼承
創(chuàng)建類褂萧,kind欄選Class,可通過鍵盤上下鍵切換類型
主構(gòu)造函數(shù)
//普通類
class Invoice {
}
//帶主構(gòu)造函數(shù)的類
class Person constructor(firstName: String) {
}
//如果主構(gòu)造函數(shù)沒有注解或可見性說明葵萎,則 constructor 關(guān)鍵字是可以省略的
class Person(firstName: String) {
}
//如果構(gòu)造函數(shù)有注解或可見性聲明机杜,則 constructor 關(guān)鍵字是不可少的魁衙,并且可見性應(yīng)該在前
class Customer public @inject constructor (name: String) {...}
//主構(gòu)造函數(shù)不能包含任意代碼喜庞。初始化代碼可以放在以 init 做前綴的初始化塊內(nèi)
class Customer(name: String) {
init {
println("Customer initialized with value ${name}")
}
}
//主構(gòu)造函數(shù)的參數(shù)可以用在初始化塊內(nèi)案狠,也可以用在類的屬性初始化聲明處
class Customer(name: String) {
val customerName = name
}
//主構(gòu)造函數(shù)中可以同時聲明屬性并進(jìn)行初始化
//類如果沒有類體可以省略大括號
class Customer(val customerName: String)
//在class前面加data修飾,Kotlin會給該類自動生成copy壳坪、toString舶得、hashCode、equals四個方法
data class Customer(val customerName: String)
- Kotlin中被val修飾的屬性會自動生成get方法爽蝴,被var修飾的屬性會自動生成get和set方法沐批,如上面的customerName屬性是存在自動生成的get屬性的
class KtTest {
var name = ""
//替換自動生成的set和get方法
set(value) {
field = value.toLowerCase()
}
get() {
return field.toUpperCase()
}
}
二級構(gòu)造函數(shù)
//類也可以有二級構(gòu)造函數(shù),需要加前綴 constructor
//如果類有主構(gòu)造函數(shù)蝎亚,每個二級構(gòu)造函數(shù)都要直接或間接通過另一個二級構(gòu)造函數(shù)代理主構(gòu)造函數(shù)九孩。在同一個類中代理另一個構(gòu)造函數(shù)使用 this 關(guān)鍵字
class Person (firstName: String) {
constructor(firstName: String, secondName: String) : this(firstName){
...
}
}
/*在 JVM 虛擬機中,如果主構(gòu)造函數(shù)的所有參數(shù)都有默認(rèn)值发框,
編譯器會生成一個附加的無參的構(gòu)造函數(shù)躺彬,這個構(gòu)造函數(shù)會直接使用默認(rèn)值。
這使得 Kotlin 可以更簡單的使用無參構(gòu)造函數(shù)來創(chuàng)建類實例的庫*/
class Customer(val customerName: String = "")
//創(chuàng)建實例, Kotlin 沒有 new 關(guān)鍵字
val c = Customer()
val p = Person("小明")
繼承
- Kotlin 中所有的類都有共同的父類 Any 梅惯,它是一個沒有父類聲明的類的默認(rèn)父類:
class Example // 隱式繼承于 Any
- Any 不是 java.lang.Object宪拥;事實上它除了 equals(),hashCode()以及toString()外沒有任何成員了。
- 繼承一個明確的父類铣减,需要在類頭后加冒號再加父類:
//open注解與java中的final相反:它允許別的類繼承這個類她君。默認(rèn)情形下,kotlin 中所有的類都是 final
//如果類有主構(gòu)造函數(shù)葫哗,則父類必須在主構(gòu)造函數(shù)中使用參數(shù)立即初始化缔刹。
open class Base(p: Int)
class Derived(p: Int) : Base(p)
/*如果類沒有主構(gòu)造函數(shù)球涛,則必須在每一個構(gòu)造函數(shù)中用 super 關(guān)鍵字初始化基類,
或者代理另一個構(gòu)造函數(shù)做這件事校镐。注意在這種情形中不同的二級構(gòu)造函數(shù)
可以調(diào)用基類不同的構(gòu)造方法:*/
class MyView : View {
constructor(ctx: Context) : super(ctx) {
}
constructor(ctx: Context, attrs: AttributeSet) : super(ctx,attrs) {
}
}
復(fù)寫方法
//在 kotlin 中堅持做明確的事亿扁,不像 java ,kotlin 需要把可以復(fù)寫的成員都明確注解出來鸟廓,并且重寫它們:
open class Base {
open fun v() {}
fun nv() {}
}
//對于有無參數(shù)構(gòu)造方法的父類从祝,繼承時可以使用無參構(gòu)造方法立即初始化
class Derived() : Base() {
override fun v() {}
}
對于 Derived.v() 來說override注解是必須的。如果沒有加的話肝箱,編譯器會提示哄褒。如果沒有open注解,像 Base.nv() ,在子類中聲明一個同樣的函數(shù)是不合法的煌张,要么加override要么不要復(fù)寫。在 final 類(沒加open注解的類)中退客,open 類型的成員是不允許的骏融。
- 標(biāo)記為override的成員是open的,它可以在子類中被復(fù)寫萌狂。如果你不想被重寫就要加 final:
open class AnotherDerived() : Base() {
final override fun v() {}
}
復(fù)寫屬性
open class Foo {
open val x: Int = 3
}
class Bar1 : Foo() {
override val x: Int = 9
}
//主構(gòu)造函數(shù)中復(fù)寫屬性档玻,使用override關(guān)鍵字作為屬性聲明的一部分
class Bar2(override val x: Int) : Foo() {
}
復(fù)寫時重名方法規(guī)則
open class A {
open fun f () { print("A") }
fun a() { print("a") }
}
interface B {
fun f() { print("B") } // 接口的成員變量默認(rèn)是 open 的
fun b() { print("b") }
}
class C() : A() , B {
// 編譯器不知道應(yīng)該選哪個f方法,會要求復(fù)寫f()
override fun f() {
super<A>.f() // 調(diào)用 A.f()
super<B>.f() // 調(diào)用 B.f()
}
}
抽象類
- 一個類或一些成員可能被聲明成 abstract 茫藏。一個抽象方法在它的類中沒有實現(xiàn)方法误趴。記住我們不用給一個抽象類或函數(shù)添加 open 注解,它默認(rèn)是帶著的务傲。
- 我們可以用一個抽象成員去復(fù)寫一個帶 open 注解的非抽象方法凉当,如下所示。
open class Base {
open fun f() {}
}
abstract class Derived : Base() {
override abstract fun f()
}
3. 接口
- Kotlin接口中的方法是可以有方法體默認(rèn)實現(xiàn)的
interface MyInterface {
fun bar()
fun foo() {
//函數(shù)體是可選的
println("foo")
}
}
//接口的實現(xiàn)
//一個類或?qū)ο罂梢詫崿F(xiàn)一個或多個接口售葡,用,隔開
class Child : MyInterface {
//沒有默認(rèn)實現(xiàn)的方法必須重寫
override fun bar () {
//函數(shù)體
}
}
- 接口中的屬性
//與Java中接口中屬性必須為常量不一樣的是Kotlin接口中的屬性可以是抽象的
interface MyInterface {
//抽象的屬性
val property: Int // abstract
//提供訪問器的實現(xiàn)的屬性看杭,和Java中常量類似
val propertyWithImplementation: String
get() = "foo"
fun foo() {
print(property)
}
}
//實現(xiàn)接口
class Child : MyInterface {
override val property: Int = 29
}
4. 邏輯控制
4.1 條件判斷
if...else...
fun test() {
val a = 100
val b = 87
//if else會將執(zhí)行最后一行代碼的結(jié)果作為返回值返回
val bigger = if (a > b) {
a
} else {
b
}
//只有一行代碼方法體的括號可以去掉
val bigger1 = if (a>b) a else b
}
when(替換Java中switch,比之強大很多)
//when里面可以傳任何對象
fun test(arg: Any) {
when (arg) {
0 -> print("OK")
in 1..8 -> print("1..8")
"OK", "Success" -> print("OK")
"Hello" -> {
println("Hello")
print("World!")
}
is Button -> print("is Button")
else -> {
print("End")
}
}
}
4.2 循環(huán)
for循環(huán)
//Kotlin中取消了Java中的for i循環(huán)
fun loopTest() {
//for in循環(huán)
for (i in 0..100) { //0<=i<=100
}
for (i in 0 until 100) { //0<=i<100
}
val list = listOf("a", "b", "c")
for (i in list) {
print(i)//a b c
}
val array = arrayListOf("1", "2", "3")
for (i in array.indices) {
println(array[i])
}
//區(qū)間寫法
for (i in IntRange(1, array.size - 1)) {
println(array[i])
}
//it為集合對象
list.forEach {
print(it) //a b c
}
//index為集合下標(biāo)挟伙,s為集合對象
list.forEachIndexed { index, s ->
}
}
while循環(huán)楼雹、do...while循環(huán)和Java中完全一樣,這里就省略了
5. 空指針防護
- 為什么需要空指針防護
空指針異常時我們平時開發(fā)中排在第一的異常尖阔。如果一個對象可以是null贮缅,則我們需要明確地指定它,然后在使用它之前檢查它是否是null介却。有了空指針防護后就可以節(jié)約很多調(diào)試空指針異常的時間谴供,解決掉null引發(fā)的bug。
//main函數(shù)
fun main(args: Array<String>) {
//getContentLength(null) //報錯:Null can not be a value of a non-null type String
getContentLength1(null)//不報錯
}
fun getContentLength(content: String): Int {
return content.length
}
fun printUpperCase(content: String?) {
//?.表示如果不為空則執(zhí)對象方法筷笨,下面是將字符串轉(zhuǎn)換成大寫憔鬼,如果為空則不執(zhí)行并返回null
println(content?.toUpperCase())
}
//參數(shù)后加?表示允許賦null
fun getContentLength1(content: String?): Int {
//return content.length //報錯Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String龟劲?
//return content?.length//這里用?.是不行的,因為返回值是Int非空轴或,當(dāng)返回null時會類型不匹配
//解決辦法1
return if (content == null) 0 else content.length
//解決辦法2昌跌,?:表示優(yōu)先前面表達(dá)式結(jié)果,當(dāng)前面表達(dá)式結(jié)果為空是則取后面結(jié)果0
return content?.length ?: 0
//解決辦法3,!!.強制去除非空檢查照雁,當(dāng)你確信參數(shù)為非空時可以這樣處理
return content!!.length
}
6. lambda表達(dá)式
7. 使用Kotlin開發(fā)Android程序
- activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginTop="8dp"
android:text="Button2"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/button" />
</android.support.constraint.ConstraintLayout>
- MainActivity.kt
package com.lg.www.kotlinstudy
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
class MainActivity : AppCompatActivity() {
var button: Button? = null
//lateinit延遲初始化關(guān)鍵字,一定要保證調(diào)用該對象方法之前該對象已經(jīng)初始化完成不為空了
lateinit var button2: Button
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
button = findViewById(R.id.button)
//?.解決為null問題
button?.text = "abc"
button?.setOnClickListener {
}
//let方法蚕愤,在let結(jié)構(gòu)圖/代碼塊里面可以執(zhí)行和這個對象相關(guān)的所有操作語句
//比如下面結(jié)構(gòu)體里面可以把所有和button相關(guān)的全部代碼引用放里面執(zhí)行,it就是button對象在結(jié)構(gòu)體的引用
button?.let {
it.text = "def"
it.setOnClickListener {
}
}
button2.text = "123"
}
}
- 使用Kotlin Android Extensions庫來優(yōu)化綁定布局饺蚊,谷歌推薦方式
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
//引入Activity對應(yīng)的布局的xml
//此后萍诱,我們就可以在 setContentView 被調(diào)用后訪問這些view,屬性的名字就是XML中對應(yīng)view的id
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
button.text = "abc"
button2.text = "123"
}
}
8. Java和Kotlin寫法對比
名稱 | Java | Kotlin |
---|---|---|
對象 | MainActivity.this | this@MainActivity |
類 | MainActivity.class | MainActivity::class.java |
靜態(tài)常量 | static final String text = ""; | const val text = ""(需要注意的是要把靜態(tài)變量定義在類上方) |
比較類型 | if ("" instanceof String) {} | if ("" is String) {} |
比較字符串 | equals | ==(Kotlin 對字符串比較的寫法進(jìn)行優(yōu)化了,其他類型對象對比還是要用 equals 方法) |