Kotlin 新特性你真的了解嗎?
點(diǎn)擊“開發(fā)者技術(shù)前線”,選擇“星標(biāo)”
讓一部分開發(fā)者先看到未來
前言
一.基本語法
1.1 變量聲明
/** * 學(xué)生類 */
class Student {
//可變變量聲明關(guān)鍵字var
var name: String = "小明"
//不指定變量類型的隱式聲明
var age = 10
//只讀變量聲明關(guān)鍵字val
val sex: String = "男"
fun learn() {
print("$name is learning")
}
}
1.2 語句
when表達(dá)式
//when語句可以作為表達(dá)式,符合條件的分支就是整個(gè)表達(dá)式的值
val b = when (num) {
in 0..9 -> {true}
else -> { false }
}
in關(guān)鍵字的使用
//判斷是否在區(qū)間內(nèi)
if (num in 1..9) {
print("ok")
}
//不在區(qū)間內(nèi)
if (num !in 1..9) {
print("no")
}
//遍歷數(shù)組
for (name in names) {
print(name)
}
//判斷name是否在數(shù)組內(nèi)
if (name in names) {
print("ok")
}
類型轉(zhuǎn)換
fun foo(o: Any): Int {
if (o is String) {
//判斷完類型之后,o會(huì)被自動(dòng)轉(zhuǎn)換為String類型
return o.length
}
//可以使用!is來取反
if (o !is String) {
return 0
}
return 0
}
空值檢測(cè)
1.3 函數(shù)聲明
fun plus(x: Int, y: Int) : Int {
return x + y
}
Kotlin中方法聲明的關(guān)鍵字是fun,聲明的定義格式為:
?
可見性修飾符 fun 函數(shù)名(參數(shù)名:類型,...) : 返回值類型{ 函數(shù)體 }
1.如果函數(shù)體內(nèi)實(shí)現(xiàn)很簡(jiǎn)單只有一行代碼那么函數(shù)也可以這樣寫
fun plus(x: Int, y: Int): Int = x + y
fun plus(x: Int, y: Int) = x + y
1.4 函數(shù)的默認(rèn)參數(shù)
fun plus(x: Int, y: Int = 10) : Int {
return x + y
}
可變參數(shù)
//java中,可變參數(shù)使用...表示
public void selectCourse(String... strArray){
}
//kotlin中,可變參數(shù)使用vararg關(guān)鍵字表示
fun selectCourse(vararg strArray: String?) {
}
二.類與對(duì)象
2.1 類的構(gòu)造函數(shù)
//聲明帶一個(gè)參數(shù)的主構(gòu)造函數(shù)
class Person constructor(name:String){
init {
//初始化的代碼可以放到init初始化塊中
//初始化塊是主構(gòu)造函數(shù)的一部分,因此所有的初始化塊中的代碼都會(huì)在次構(gòu)造函數(shù)體之前執(zhí)行
}
//次級(jí)構(gòu)造函數(shù)委托給主構(gòu)造函數(shù)直接委托
constructor(name:String,parent:Person):this(name){
}
//委托給別的次級(jí)構(gòu)造函數(shù)間接委托
constructor(name:String,parent:Person,age:Int):this(name,parent){
}
}
2.2 類的繼承
如果派生類有一個(gè)主構(gòu)造函數(shù),其基類型則必須用基類的主構(gòu)造函數(shù)參數(shù)初始化
class Derived(p: Int) : Base(p){}
class MyView : View {
constructor(ctx: Context) : super(ctx)
constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs)
}
//對(duì)于可覆蓋的成員需要使用顯示修飾符open
open class Rectangle {
open var length = 0
open fun draw() { /*.......*/
}
}
interface Polygon {
//接口中的成員默認(rèn)open
fun draw() { /*........*/
}
}
class Square() : Rectangle(), Polygon {
override fun draw() {
super<Rectangle>.draw() //調(diào)用Rectangle.draw()
super<Polygon>.draw() //調(diào)用Polygon.draw()
}
}
2.3 類的屬性
2.3.1 getter與setter
val修飾的屬性只有g(shù)etter
var age: Int = 11
get() {
return field
}
set(value) {
field = value + 1
}
2.3.2 幕后字段
class Person {
var name = ""
set(value) {
this.name = value
}
}
public final class Person {
private String name = "Paul";
public final String getName() {
return this.name;
}
public final void setName( String value) {
this.setName(value);
}
}
在Kotlin中,如果屬性至少一個(gè)訪問器使用默認(rèn)實(shí)現(xiàn),那么Kotlin會(huì)自動(dòng)提供幕后字段,用關(guān)鍵字field表示,幕后字段主要用于自定義getter和setter中,并且只能在getter和setter中訪問。
上面代碼應(yīng)該修改成
class Person {
var name = ""
set(value) {
filed = value
}
}
使用默認(rèn)getter/setter的屬性,一定有幕后字段。對(duì)于var屬性來說,只要getter/setter中有一個(gè)使用默認(rèn)實(shí)現(xiàn),就會(huì)生成幕后字段; 在自定義getter/setter中使用了field的屬性。
2.3.3 常量
//每次獲取currentTimeMills都是不同的
val currentTimeMills: Long
get() {
return System.currentTimeMillis()
}
class Person {
companion object{
//使用const修飾符
const val TAG = "Person"
}
//使用@JvmField注解方式
//其內(nèi)部原理是抑制編譯器生成相應(yīng)的getter方法并且無法重寫val的get方法
@JvmField
val TAG = "Person"
}
2.3.4 屬性延遲初始化
2.4 內(nèi)部類
class Outer {
private val b: Int = 1
inner class Inner {
fun foo(): Int = b
}
}
val d = Outer().Inner().foo() // ===1
2.5 數(shù)據(jù)類
主構(gòu)造函數(shù)至少有一個(gè)參數(shù) 主構(gòu)造函數(shù)的參數(shù)需要顯示的標(biāo)記為val或者var 數(shù)據(jù)類不能是抽象,開放,密封或者內(nèi)部的
類中聲明的屬性在toString(),equals(),hashCode()以及copy()的實(shí)現(xiàn)中被排除
data class Person(val name: String) {
var age: Int = 0
}
fun foo() {
val person1 = Person("John")
val person2 = Person("John")
person1.age = 10
person2.age = 20
//person1和person2雖然有不同的年齡但是會(huì)視為相等
person1.equals(person2) //true
}
2.6 枚舉類
enum class Direction {
NORTH, SOUTH, WEST, EAST
}
enum class Color(val rgb: Int) {
RED(0xFF0000),
GREEN(0x00FF00),
BLUE(0x0000FF)
}
要實(shí)現(xiàn)枚舉常量的匿名類,則必須提供一個(gè)抽象方法(必須重寫的方法)。且該方法定義在枚舉類內(nèi)部。而且必須在枚舉變量的后面。 枚舉變量之間使用逗號(hào)(,)分割開。但是最后一個(gè)枚舉變量必須使用分號(hào)結(jié)束。不然定義不了抽象方法
enum class Color(val rgb: Int) {
RED(0xFF0000) {
override fun print() {
print("red")
}
},
GREEN(0x00FF00) {
override fun print() {
print("green")
}
},
BLUE(0x0000FF) {
override fun print() {
print("blue")
}
};
abstract fun print()
}
fun main() {
Color.BLUE.print()
}
每個(gè)枚舉常量都包含兩個(gè)屬性:name(枚舉常量名)和ordinal(枚舉常量位置) 提供了values()和valueOf()方法來檢測(cè)指定的名稱與枚舉類中定義的任何枚舉常量是否匹配。
2.7 委托
2.7.1 類委托
//創(chuàng)建接口
interface Base {
fun print()
}
//實(shí)現(xiàn)此接口的被委托的類
class BaseImpl(val x: Int) : Base {
override fun print() {
print(x)
}
}
//通過關(guān)鍵字by 建立委托類
class Agent(b: Base) : Base by b
fun main(args: Array<String>) {
val b = BaseImpl(1)
Agent(b).print() // 輸出 1
}
如果Agent()中覆蓋實(shí)現(xiàn)了print()方法,那么將使用覆蓋的實(shí)現(xiàn),而不是委托對(duì)象中的實(shí)現(xiàn)
class Agent(b: Base) : Base by b {
override fun print() {
print("123")
}
}
fun main(args: Array<String>) {
val b = BaseImpl(1)
Agent(b).print() // 輸出 123
}
2.7.2 屬性委托
class Delegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return "thank you for delegating '${property.name}' to me"
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
print("$value has been assigned to '${property.name}'")
}
}
class Example {
var p: String by Delegate()
}
fun foo() {
val e = Example()
print(e.p) // thank you for delegating 'p' to me
e.p = "new" // new has been assigned to 'p'
}
延遲屬性Lazy
class LazySample {
val lazyStr: String by lazy {
print("init")
"123"
}
}
fun main(args:Array<String>){
val sample = LazySample()
print("lazy = ${sample.lazyStr}")
print("lazy = ${sample.lazyStr}")
}
// 輸出
// init
// lazy = 123
// lazy = 123
屬性非空強(qiáng)校驗(yàn)
var name: String by Delegates.notNull()
fun init(name: String) {
this.name = name
}
fun main(args: Array<String>) {
val student = Student()
//初始化要在使用之前不然會(huì)報(bào)異常->IllegalStateException
student.init("張三")
print(student.name)
}
可觀察屬性
private var name: String by Delegates.observable("oldValue") { property, oldValue, newValue ->
print("${property.name} 屬性變化: $oldValue -> $newValue")
}
private var age: Int by Delegates.observable(0) { property, oldValue, newValue ->
print("${property.name} 屬性變化: $oldValue -> $newValue")
}
private fun print() {
print("name = $name")
print("age = $age")
}
fun main(args: Array<String>) {
print()
name = "Bob"
age = -1 //小于0,修改失敗
print()
age = 28
print()
}
/*
* name = oldValue
* age = 0
* name 屬性變化: oldValue -> Bob
* age 屬性變化: 0 -> -1
* name = Bob
* age = 0
* age 屬性變化: 0 -> 28
* name = Bob
* ag
三.函數(shù)
3.1 局部函數(shù)
fun outer(str: String) {
fun inner(index: Int) {
str.substring(0, index)
}
inner(2)
}
3.2 函數(shù)類型
所有函數(shù)類型都有一個(gè)圓括號(hào)括起來的參數(shù)類型列表以及一個(gè)返回類型:(A, B) -> C 表示接受類型分別為 A 與 B 兩個(gè)參數(shù)并返回一個(gè) C 類型值的函數(shù)類型 函數(shù)類型可以有一個(gè)額外的接收者類型,它在表示法中的點(diǎn)之前指定:類型 A.(B) -> C 表示可以在 A 的接收者對(duì)象上以一個(gè) B 類型參數(shù)來調(diào)用并返回一個(gè) C 類型值的函數(shù). 掛起函數(shù)屬于特殊種類的函數(shù)類型,它的表示法中有一個(gè) suspend 修飾符 ,例如 suspend () -> Unit 或者 suspend A.(B) -> C 函數(shù)類型可以使用圓括號(hào)進(jìn)行接合:(Int) -> ((Int) -> Unit) 箭頭表示法是右結(jié)合的,(Int) -> (Int) -> Unit 與(Int) -> ((Int) -> Unit)等價(jià),但不等于 ((Int) -> (Int)) -> Unit。 typealias ClickHandler = (Button, ClickEvent) -> Unit 通過使用類型別名給函數(shù)類型起一個(gè)別稱
//(A,B) -> C
val fun1: (String, String) -> Unit = { s1, s2 ->
print("$s1 and $s2")
}
//A.(B) -> C
val fun2: String.(String) -> Unit = { s ->
print("$this $s")
}
fun foo() {
fun1("123", "456")
fun1.invoke("123", "456")
fun2("123", "456")
fun2.invoke("123", "456")
"123".fun2("456")
}
3.3 Lambda表達(dá)式
Lambda表達(dá)式總是在花括號(hào)中,參數(shù)聲明放在花括號(hào)內(nèi),并有可選的類型標(biāo)注,函數(shù)體跟在一個(gè)->符號(hào)之后,如果推斷出的該Lambda的返回類型不是Unit,那么最后一個(gè)表達(dá)式會(huì)視為返回值。
val sum = { x: Int, y: Int -> x + y }
fun sum(m: Int, n: Int, param: (x: Int, y: Int) -> Int): Int {
return param.invoke(m, n)
}
fun main(args: Array<String>) {
sum(1, 2) { x, y -> x + y }
}
fun sum(m: Int, n: Int, param: (x: Int, y: Int) -> Int): Int {
return param.invoke(m, n)
}
fun main(args: Array<String>) {
sum(1, 2) { x, _ -> x + 10 }
}
3.4 匿名函數(shù)
fun(x: Int, y: Int): Int = x + y
3.5 高階函數(shù)
fun sum(m: Int, n: Int, param: (x: Int, y: Int) -> Int): Int {
return param.invoke(m, n)
}
fun main(args: Array<String>) {
//函數(shù)類型作為參數(shù)
val param: (x: Int, y: Int) -> Int = { x, y -> x + y }
sum(1, 2, param)
}
fun sum(): (x: Int, y: Int) -> Int {
return { x, y -> x + y }
}
fun main(args: Array<String>) {
//函數(shù)類型作為返回值類型
sum().invoke(1, 2)
}
3.6 中綴表示法
它們必須是成員函數(shù)或擴(kuò)展函數(shù); 它們必須只有一個(gè)參數(shù); 其參數(shù)不得接受可變數(shù)量的參數(shù)且不能有默認(rèn)值。
infix fun Int.sum(x: Int): Int {
return 1
}
fun main(args: Array<String>) {
//用中綴表示法調(diào)用該函數(shù)
1 sum 2
//等同于調(diào)用
1.sum(2)
}
3.7 內(nèi)聯(lián)函數(shù)
fun main(args: Array<String>) {
print("start")
show("123")
print("end")
}
inline fun show(str: String) {
print(str)
}
public final void main(@NotNull String[] args) {
System.out.print("start");
System.out.print("123");
System.out.print("end");
}
public final void show(@NotNull String str) {
System.out.print(str);
}
public final void main(@NotNull String[] args) {
System.out.print("start");
this.show("123")
System.out.print("end");
}
public final void show(@NotNull String str) {
System.out.print(str);
}
3.8 標(biāo)準(zhǔn)庫函數(shù)
| 函數(shù)名稱 | 定義 | 功能 |
|---|---|---|
| run | public inline fun | 調(diào)用run函數(shù)塊。返回值為函數(shù)塊最后一行,或者指定return表達(dá)式 |
| apply | public inline fun | 調(diào)用某對(duì)象的apply函數(shù),在函數(shù)塊內(nèi)可以通過 this 指代該對(duì)象。返回值為該對(duì)象自己 |
| let | public inline fun <T, R> T.let(block: (T) -> R): R = block(this) | 調(diào)用某對(duì)象的let函數(shù),則該對(duì)象為函數(shù)的參數(shù)。在函數(shù)塊內(nèi)可以通過 it 指代該對(duì)象。返回值為函數(shù)塊的最后一行或指定return表達(dá)式 |
| also | public inline fun | 調(diào)用某對(duì)象的also函數(shù),則該對(duì)象為函數(shù)的參數(shù)。在函數(shù)塊內(nèi)可以通過 it 指代該對(duì)象。返回值為該對(duì)象自己 |
| with | public inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block() | 將某對(duì)象作為函數(shù)的參數(shù),在函數(shù)塊內(nèi)可以通過 this 指代該對(duì)象。返回值為函數(shù)塊的最后一行或指定return表達(dá)式 |
總結(jié)
— 完 —
點(diǎn)這里??關(guān)注我,記得標(biāo)星呀~
前線推出學(xué)習(xí)交流一定要備注:研究/工作方向+地點(diǎn)+學(xué)校/公司+昵稱(如JAVA+上海
掃碼加小編微信,進(jìn)群和大佬們零距離
后臺(tái)回復(fù)“電子書” “資料” 領(lǐng)取一份干貨,數(shù)百面試手冊(cè)等你 開發(fā)者技術(shù)前線 ,匯集技術(shù)前線快訊和關(guān)注行業(yè)趨勢(shì),大廠干貨, 是開發(fā)者經(jīng)歷和成長(zhǎng)的優(yōu)秀指南。
評(píng)論
圖片
表情
