最近在學(xué)kotlin,這是本人看菜鳥教程后所做的筆記,很多會(huì)內(nèi)容和菜鳥教程重復(fù),僅供參考
基礎(chǔ)語法
包聲明
//類在包的位置
package com.demo.main
import java.util.*
函數(shù)定義
函數(shù)定義使用關(guān)鍵字 fun,參數(shù)格式為:參數(shù) : 類型
fun printSum(a: Int,b: Int){
println(a+b)
}
無返回值的函數(shù)
public fun printSum(a: Int, b: Int) {
print(a + b)
}
可變長參數(shù)函數(shù)
fun vars(vararg a:String){
for(str in a){
println(str)
}
}
定義常量與變量
可變變量定義:var 關(guān)鍵字
var <標(biāo)識(shí)符> : <類型> = <初始化值>
不可變變量定義:val關(guān)鍵字,只能賦值一次的變量Java中final修飾的變量)
val <標(biāo)識(shí)符> : <類型> = <初始化值>
注釋
// 這是一個(gè)單行注釋
/* 這是一個(gè)多行的
塊注釋。 */
字符串模板
$ 表示一個(gè)變量名或者變量值
$varName 表示變量值
${varName.fun()} 表示變量的方法返回值:
數(shù)據(jù)類型
| 類型 | 位寬度 |
|---|---|
| Double | 64 |
| Float | 32 |
| Long | 64 |
| Int | 32 |
| Short | 16 |
| Byte | 8 |
比較兩個(gè)數(shù)字
在 Kotlin 中,三個(gè)等號(hào) === 表示比較對(duì)象地址,兩個(gè) == 表示比較兩個(gè)值大小。
類型轉(zhuǎn)換
由于不同的表示方式,較小類型并不是較大類型的子類型,較小的類型不能隱式轉(zhuǎn)換為較大的類型。 這意味著在不進(jìn)行顯式轉(zhuǎn)換的情況下我們不能把 Byte 型值賦給一個(gè) Int 變量。
val b: Byte = 10 // OK, 字面值是靜態(tài)檢測的
val i: Int = b // 錯(cuò)誤
我們可以代用其toInt()方法。
val b: Byte = 1 // OK, 字面值是靜態(tài)檢測的
val i: Int = b.toInt() // OK
每種數(shù)據(jù)類型都有下面的這些方法,可以轉(zhuǎn)化為其它的類型:
toByte(): Byte
toShort(): Short
toInt(): Int
toLong(): Long
toFloat(): Float
toDouble(): Double
toChar(): Char
有些情況下也是可以使用自動(dòng)類型轉(zhuǎn)化的,前提是可以根據(jù)上下文環(huán)境推斷出正確的數(shù)據(jù)類型而且數(shù)學(xué)操作符會(huì)做相應(yīng)的重載。例如下面是正確的:
val l = 1L + 3 // Long + Int => Long
條件控制
IF 表達(dá)式
一個(gè) if 語句包含一個(gè)布爾表達(dá)式和一條或多條語句。
var max = a
if (a < b) max = b
// 使用 else
var max: Int
if (a > b) {
max = a
} else {
max = b
}
// 作為表達(dá)式
val max = if (a > b) a else b
When 表達(dá)式
when最簡單的形勢(shì)如下:
fun whenTest(a : Int){
when(a){
1->{
println(1)
}
2->{
println(2)
}
3->{
println(3)
}
else->{
println("其他")
}
}
}
when 也可以用來取代 if-else if鏈。 如果不提供參數(shù),所有的分支條件都是簡單的布爾表達(dá)式,而當(dāng)一個(gè)分支的條件為真時(shí)則執(zhí)行該分支:
when {
x.isOdd() -> print("x is odd")
x.isEven() -> print("x is even")
else -> print("x is funny")
}
循環(huán)控制
For 循環(huán)
循環(huán)體可以是一個(gè)代碼塊:
for (item: Int in ints) {
// ……
}
while 與 do...while 循環(huán)
while是最基本的循環(huán),它的結(jié)構(gòu)為:
while( 布爾表達(dá)式 ) {
//循環(huán)內(nèi)容
}
do…while 循環(huán) 對(duì)于 while 語句而言,如果不滿足條件,則不能進(jìn)入循環(huán)。但有時(shí)候我們需要即使不滿足條件,也至少執(zhí)行一次。
do…while 循環(huán)和 while 循環(huán)相似,不同的是,do…while 循環(huán)至少會(huì)執(zhí)行一次。
do {
//代碼語句
}while(布爾表達(dá)式);
返回和跳轉(zhuǎn)
Kotlin 有三種結(jié)構(gòu)化跳轉(zhuǎn)表達(dá)式:
- return。默認(rèn)從最直接包圍它的函數(shù)或者匿名函數(shù) 返回。
- break。終止最直接包圍它的循環(huán)。
- continue。繼續(xù)下一次最直接包圍它的循環(huán)。
類和對(duì)象
類定義
Kotlin 中使用關(guān)鍵字 class 聲明類,后面緊跟類名:
class Runoob { // 類名為 Runoob
// 大括號(hào)內(nèi)是類體構(gòu)成
}
也可以定義一個(gè)空類:
class Empty
屬性定義
類的屬性可以用關(guān)鍵字 var 聲明為可變的,否則使用只讀關(guān)鍵字 val 聲明為不可變。
我們可以像使用普通函數(shù)那樣使用構(gòu)造函數(shù)創(chuàng)建類實(shí)例:
val site = Runoob() // Kotlin 中沒有 new 關(guān)鍵字
要使用一個(gè)屬性,只要用名稱引用它即可
site.name // 使用 . 號(hào)來引用
site.url
主構(gòu)造器
主構(gòu)造器中不能包含任何代碼,初始化代碼可以放在初始化代碼段中,初始化代碼段使用 init 關(guān)鍵字作為前綴。
class Person constructor(firstName: String) {
init {
println("FirstName is $firstName")
}
}
次構(gòu)造函數(shù)
類也可以有二級(jí)構(gòu)造函數(shù),需要加前綴 constructor:
class Person {
constructor(parent: Person) {
parent.children.add(this)
}
}
如果類有主構(gòu)造函數(shù),每個(gè)次構(gòu)造函數(shù)都要,或直接或間接通過另一個(gè)次構(gòu)造函數(shù)代理主構(gòu)造函數(shù)。在同一個(gè)類中代理另一個(gè)構(gòu)造函數(shù)使用 this 關(guān)鍵字:
class Person(val name: String) {
constructor (name: String, age:Int) : this(name) {
// 初始化...
}
}
抽象類
抽象是面向?qū)ο缶幊痰奶卣髦?,類本身,或類中的部分成員,都可以聲明為abstract的。抽象成員在類中不存在具體的實(shí)現(xiàn)。
abstract class father{
abstract fun f()
}
class childs : father() {
override fun f() {
}
}
嵌套類
class Outer { // 外部類
private val bar: Int = 1
class Nested { // 嵌套類
fun foo() = 2
}
}
fun main(args: Array<String>) {
val demo = Outer.Nested().foo() // 調(diào)用格式:外部類.嵌套類.嵌套類方法/屬性
println(demo) // == 2
}
內(nèi)部類
內(nèi)部類使用 inner 關(guān)鍵字來表示。
內(nèi)部類會(huì)帶有一個(gè)對(duì)外部類的對(duì)象的引用,所以內(nèi)部類可以訪問外部類成員屬性和成員函數(shù)。
class Outer {
private val bar: Int = 1
var v = "成員屬性"
/**嵌套內(nèi)部類**/
inner class Inner {
fun foo() = bar // 訪問外部類成員
fun innerTest() {
var o = this@Outer //獲取外部類的成員變量
println("內(nèi)部類可以引用外部類的成員,例如:" + o.v)
}
}
}
fun main(args: Array<String>) {
val demo = Outer().Inner().foo()
println(demo) // 1
val demo2 = Outer().Inner().innerTest()
println(demo2) // 內(nèi)部類可以引用外部類的成員,例如:成員屬性
}
匿名內(nèi)部類
使用對(duì)象表達(dá)式來創(chuàng)建匿名內(nèi)部類:
class Test {
var v = "成員屬性"
fun setInterFace(test: TestInterFace) {
test.test()
}
}
/**
* 定義接口
*/
interface TestInterFace {
fun test()
}
fun main(args: Array<String>) {
var test = Test()
/**
* 采用對(duì)象表達(dá)式來創(chuàng)建接口對(duì)象,即匿名內(nèi)部類的實(shí)例。
*/
test.setInterFace(object : TestInterFace {
override fun test() {
println("對(duì)象表達(dá)式創(chuàng)建匿名內(nèi)部類的實(shí)例")
}
})
}
類的修飾符
類的修飾符包括 classModifier 和accessModifier:
- classModifier: 類屬性修飾符,標(biāo)示類本身特性。
abstract // 抽象類
final // 類不可繼承,默認(rèn)屬性
enum // 枚舉類
open // 類可繼承,類默認(rèn)是final的
annotation // 注解類
- accessModifier: 訪問權(quán)限修飾符
private // 僅在同一個(gè)文件中可見
protected // 同一個(gè)文件中或子類可見
public // 所有調(diào)用的地方都可見
internal // 同一個(gè)模塊中可見
繼承
Kotlin 中所有類都繼承該 Any 類,它是所有類的超類,對(duì)于沒有超類型聲明的類是默認(rèn)超類:
class Example // 從 Any 隱式繼承
Any 默認(rèn)提供了三個(gè)函數(shù):
equals()
hashCode()
toString()
注意:Any 不是 java.lang.Object。
如果一個(gè)類要被繼承,可以使用 open 關(guān)鍵字進(jìn)行修飾。
open class father(p: Int) // 定義基類
class Derived(p: Int) : father(p)
構(gòu)造函數(shù)
如果子類有主構(gòu)造函數(shù), 則基類必須在主構(gòu)造函數(shù)中立即初始化。
open class Person(var name : String, var age : Int){// 基類
}
class Student(name : String, age : Int, var no : String, var score : Int) : Person(name, age) {
}
// 測試
fun main(args: Array<String>) {
val s = Student("Runoob", 18, "S12346", 89)
println("學(xué)生名: ${s.name}")
println("年齡: ${s.age}")
println("學(xué)生號(hào): ${s.no}")
println("成績: ${s.score}")
}
子類沒有主構(gòu)造函數(shù)
如果子類沒有主構(gòu)造函數(shù),則必須在每一個(gè)二級(jí)構(gòu)造函數(shù)中用 super 關(guān)鍵字初始化基類,或者在代理另一個(gè)構(gòu)造函數(shù)。初始化基類時(shí),可以調(diào)用基類的不同構(gòu)造方法。
/**用戶基類**/
open class Person(name:String){
/**次級(jí)構(gòu)造函數(shù)**/
constructor(name:String,age:Int):this(name){
//初始化
println("-------基類次級(jí)構(gòu)造函數(shù)---------")
}
}
/**子類繼承 Person 類**/
class Student:Person{
/**次級(jí)構(gòu)造函數(shù)**/
constructor(name:String,age:Int,no:String,score:Int):super(name,age){
println("-------繼承類次級(jí)構(gòu)造函數(shù)---------")
println("學(xué)生名: ${name}")
println("年齡: ${age}")
println("學(xué)生號(hào): ${no}")
println("成績: ${score}")
}
}
接口
Kotlin 接口與 Java 8 類似,使用 interface 關(guān)鍵字定義接口,允許方法有默認(rèn)實(shí)現(xiàn):
interface MyInterface{
fun bar()
fun foo(){
println("foo")
}
}
class MyClass : MyInterface{
override fun bar() {
println("bar")
}
}
fun main(args:Array<String>){
var myClass=MyClass()
myClass.bar()
myClass.foo()
}
接口中的屬性
接口中的屬性只能是抽象的,不允許初始化值,接口不會(huì)保存屬性值,實(shí)現(xiàn)接口時(shí),必須重寫屬性:
interface MyInterface{
var name:String
}
class MyClass : MyInterface{
override var name="Jack"
}
fun main(args:Array<String>){
var myClass=MyClass()
println(myClass.name)
}
函數(shù)重寫
實(shí)現(xiàn)多個(gè)接口時(shí),可能會(huì)遇到同一方法繼承多個(gè)實(shí)現(xiàn)的問題。例如:
interface MyInterface2{
fun foo()
fun bar(){
println("bar2")
}
}
class MyClass : MyInterface,MyInterface2{
override fun bar() {
super<MyInterface2>.bar()
}
override fun foo() {
println("foo2")
}
}
fun main(args:Array<String>){
var myClass=MyClass()
myClass.foo()
myClass.bar()
}
當(dāng)一個(gè)有實(shí)現(xiàn)方法,一個(gè)沒有實(shí)現(xiàn)方法時(shí),默認(rèn)是沒有實(shí)現(xiàn)方法的, super<MyInterface>.foo()切換實(shí)現(xiàn)方法,兩個(gè)都有時(shí)也可以通過這個(gè)方式切換實(shí)現(xiàn)方法
泛型
泛型,即 "參數(shù)化類型",將類型參數(shù)化,可以用在類,接口,方法上。
與 Java 一樣,Kotlin 也提供泛型,為類型安全提供保證,消除類型強(qiáng)轉(zhuǎn)的煩惱。
聲明一個(gè)泛型類:
class Class<T>(t: T) {
var value = t
}
型變
Kotlin 中沒有通配符類型,它有兩個(gè)其他的東西:聲明處型變(declaration-site variance)與類型投影(type projections)。
聲明處的類型變異使用協(xié)變注解修飾符:in、out,消費(fèi)者 in, 生產(chǎn)者 out。
使用 out 使得一個(gè)類型參數(shù)協(xié)變,協(xié)變類型參數(shù)只能用作輸出,可以作為返回值類型但是無法作為入?yún)⒌念愋停?/p>
in 使得一個(gè)類型參數(shù)逆變,逆變類型參數(shù)只能用作輸入,可以作為入?yún)⒌念愋偷菬o法作為返回值的類型:
class demo<out A>(val a: A) {
fun foo(): A {
return a
}
}
class demo2<in A>(a: A) {
fun foo(a: A) {
}
}
泛型約束
我們可以使用泛型約束來設(shè)定一個(gè)給定參數(shù)允許使用的類型。
Kotlin 中使用 : 對(duì)泛型的的類型上限進(jìn)行約束。
fun <T : Comparable<T>> sort(list: List<T>) {
// ……
}
枚舉類
枚舉類最基本的用法是實(shí)現(xiàn)一個(gè)類型安全的枚舉。
枚舉常量用逗號(hào)分隔,每個(gè)枚舉常量都是一個(gè)對(duì)象。
enum class Color{
RED,BLACK,BLUE,GREEN,WHITE
}
fun main(args: Array<String>) {
var color:Color=Color.BLUE
println(Color.values())
println(Color.valueOf("RED"))
println(color.name)
println(color.ordinal)
}
對(duì)象表達(dá)式和對(duì)象聲明
對(duì)象可以繼承于某個(gè)基類,或者實(shí)現(xiàn)其他接口:
open class A(x: Int) {
public open val y: Int = x
}
interface B {
}
fun main(args:Array<String>){
val ab: A = object : A(1), B {
override val y = 15
}
println(ab.y)
}
通過對(duì)象表達(dá)式可以越過類的定義直接得到一個(gè)對(duì)象:
val site = object {
var name: String = "Jack"
}
println(site.name)
匿名對(duì)象可以用作只在本地和私有作用域中聲明的類型。如果你使用匿名對(duì)象作為公有函數(shù)的 返回類型或者用作公有屬性的類型,那么該函數(shù)或?qū)傩缘膶?shí)際類型 會(huì)是匿名對(duì)象聲明的超類型,如果你沒有聲明任何超類型,就會(huì)是 Any。在匿名對(duì)象 中添加的成員將無法訪問。
class A {
// 私有函數(shù),所以其返回類型是匿名對(duì)象類型
private fun foo() = object {
val x: String = "x"
}
// 公有函數(shù),所以其返回類型是 Any
fun Foo2() = object {
val x: String = "x"
}
fun bar() {
val x1 = foo().x // 沒問題
val x2 = publicFoo().x // 錯(cuò)誤:未能解析的引用“x”
}
}
對(duì)象聲明
Kotlin 使用 object 關(guān)鍵字來聲明一個(gè)對(duì)象。
object Test{
fun foo(){
println("Jack")
}
}
fun main(args:Array<String>){
Test.foo()
}
當(dāng)然你也可以定義一個(gè)變量來獲取獲取這個(gè)對(duì)象,當(dāng)時(shí)當(dāng)你定義兩個(gè)不同的變量來獲取這個(gè)對(duì)象時(shí),你會(huì)發(fā)現(xiàn)你并不能得到兩個(gè)不同的變量。也就是說通過這種方式,我們獲得一個(gè)單例。
object My {
var name: String = "JACK"
}
fun main(args:Array<String>){
var s1 = My
var s2 = My
s1.name = "HelloJack"
println(s1.name)
println(s2.name)
}
伴生對(duì)象
類內(nèi)部的對(duì)象聲明可以用 companion 關(guān)鍵字標(biāo)記,這樣它就與外部類關(guān)聯(lián)在一起,我們就可以直接通過外部類訪問到對(duì)象的內(nèi)部元素。
class MyClass {
companion object Factory {
fun create(): MyClass = MyClass()
}
fun test(){
println("Jack")
}
}
fun main(args:Array<String>){
val instance = MyClass.create()
instance.test()
}
我們可以省略掉該對(duì)象的對(duì)象名,然后使用 Companion 替代需要聲明的對(duì)象名:
class MyClass {
companion object {
}
}
val x = MyClass.Companion
注意:一個(gè)類里面只能聲明一個(gè)內(nèi)部關(guān)聯(lián)對(duì)象,即關(guān)鍵字 companion 只能使用一次。
類委托
類的委托即一個(gè)類中定義的方法實(shí)際是調(diào)用另一個(gè)類的對(duì)象的方法來實(shí)現(xiàn)的。
// 創(chuàng)建接口
interface Base {
fun print()
}
// 實(shí)現(xiàn)此接口的被委托的類
class BaseImpl(val x: Int) : Base {
override fun print() { print(x) }
}
// 通過關(guān)鍵字 by 建立委托類
class Derived(b: Base) : Base by b
fun main(args:Array<String>){
val b = BaseImpl(10)
Derived(b).print() // 輸出 10
}
lazy() 是一個(gè)函數(shù), 接受一個(gè) Lambda 表達(dá)式作為參數(shù), 返回一個(gè) Lazy <T> 實(shí)例的函數(shù),返回的實(shí)例可以作為實(shí)現(xiàn)延遲屬性的委托: 第一次調(diào)用 get() 會(huì)執(zhí)行已傳遞給 lazy() 的 lamda 表達(dá)式并記錄結(jié)果, 后續(xù)調(diào)用 get() 只是返回記錄的結(jié)果。
val MyLazyValue: String by lazy {
println("computed!") // 第一次調(diào)用輸出,第二次調(diào)用不執(zhí)行
"Hello"
}
fun main(args:Array<String>){
println(MyLazyValue)
println(MyLazyValue)
}
一個(gè)常見的用例是在一個(gè)映射(map)里存儲(chǔ)屬性的值。 這經(jīng)常出現(xiàn)在像解析 JSON 或者做其他"動(dòng)態(tài)"事情的應(yīng)用中。 在這種情況下,你可以使用映射實(shí)例自身作為委托來實(shí)現(xiàn)委托屬性。
class Data(val map: Map<String, Any?>) {
val name: String by map
val url: String by map
}
fun main(args:Array<String>){
val site = Data(mapOf(
"name" to "Jack1",
"url" to "Jack2"
))
// 讀取映射值
println(site.name)
println(site.url)
}