淺談Kotlin語法篇之變量和常量(二)

此次所說的是Kotlin的變量和常量,主要會對如下內容作介紹:java

  • 一、變量基本定義
  • 二、var和val的區別
  • 三、智能類型推斷
  • 四、自定義屬性訪問器
  • 五、var是可變的而val必定是不可變的嗎

1、Kotlin與Java中變量和常量 使用對比

  • 一、在Java中定義一個變量和常量
public String name = "Mikyou";//定義變量
public final int age = 18;//final定義常量
複製代碼
  • 二、在Kotlin中定義一個變量和常量
var name: String = "Mikyou"
val age: Int = 18
複製代碼

或者編程

var name = "Mikyou"
val age = 18
複製代碼

總結: 由以上的對比可得出:bash

  • 一、Kotlin定義一個變量和常量比Java更簡潔和靈活
  • 二、Kotlin中擁有類型推斷的機制,當一個變量或者常量初始化的時候能夠省略類型的聲明,它會根據初始化值的類型來做爲變量的類型。
  • 三、Kotlin聲明變量和常量都必須使用var(變量),val(常量)關鍵字開頭,而後再是名稱,最後纔是類型(若是有初始化值類型能夠直接省略)
  • 四、Kotlin相比Java默認的訪問修飾符是public,而Java中是default
  • 五、Java中通常都是以類型開頭而後再是名稱,而且類型是不能夠省略的;這樣的操做在Kotlin中是不行的。由於在Kotlin中類型是能夠省略的,也就是類型相對比較弱化的,因此Kotlin會把類型放在最後,通常含有初始化值就會把在後面的類型聲明省略。

2、Kotlin的變量和常量用法

var name: String = "Mikyou"
var address: String?//若是這個變量沒有初始化,那麼須要顯示聲明類型而且指明類型是否可null
address = "NanJing"
val age: Int = 18
複製代碼

或者app

var name = "Mikyou"
val age = 18
複製代碼
  • 一、變量和常量聲明必須以「var」和「val」關鍵字開頭。
  • 二、 變量和常量的結構: (var 或者 val) 名稱 :(分割逗號) 變量類型 = 初始化值。
  • 三、 智能類型轉換(編譯器提示爲smart cast),若是變量或常量含有初始值能夠省略類型,編譯器會默認分析出將初始化值的類型做爲變量和常量類型。
  • 四、若是變量沒有初始化,那麼須要顯示聲明類型而且須要指明類型是否可null。

3、Kotlin的自定義屬性訪問器

在Kotlin中屬性是頭等特性,它習慣於用一個屬性去替代Java中的字段和setter,getter方法。而Kotlin中的set、get訪問器至關於Java中的setter,getter方法。Kotlin有個新的特性就是能夠去定義一個類中屬性的訪問器包括setter,getter訪問器。該特性十分適用於須要通過多個屬性邏輯計算得出的一個新的屬性。那麼邏輯計算的過程操做不須要像Java同樣開一個方法來實現。能夠直接在屬性訪問器中進行邏輯運算。dom

  • 一、自定義get屬性訪問器

在Java中實現:ide

public class Rectangle {

    private float width;
    private float height;

    public Rectangle(float width, float height) {
        this.width = width;
        this.height = height;
    }

    public boolean isSquare() {//在java中實現一個方法進行相關的邏輯計算
        return width == height;
    }

}
複製代碼

在Kotlin中實現:函數式編程

class Rectangle(width: Float, height: Float) {
    val isSquare: Boolean//在Kotlin中只須要一個屬性就能解決,從新定義了isSquare的getter屬性訪問器,訪問器中能夠寫入具體邏輯代碼。
        get() {
            return width == height
        }
}
複製代碼
  • 二、自定義set屬性訪問器

在Java中實現Android中的一個自定義View中的setter方法。函數

public class RoundImageView extends ImageView {
        ...
    
    public void setBorderColor(int color) {
        mColor = color;
        invalidate();
    }
        ...
}
複製代碼

在Kotlin中實現Android中的一個自定義View中的set屬性訪問器。學習

class RoundImageView @JvmOverloads constructor(context: Context, attributeSet: AttributeSet? = null, defAttrStyle: Int = 0)
    : ImageView(context, attributeSet, defAttrStyle) {

    var mBorderColor: Int
        set(value) {//自定義set屬性訪問器
            field = value
            invalidate()
        }
}
複製代碼

4、Kotlin中的var和val區別

  • 一、var(來自於variable)可變引用。而且被它修飾的變量的值是能夠改變,具備可讀和可寫權限,至關於Java中非final的變量。
  • 二、val(來自於value)不可變引用。而且被它修飾的變量的值通常狀況初始化一遍後期不能再次不然會拋出編譯異常(這句話有待商榷,這個問題的討論和求證請看Kotlin中val不可變與可讀的討論),至關於Java中的final修飾的常量。
  • 三、在Kotlin開發過程當中儘量多的使用val關鍵字聲明全部的Kotlin的變量,僅僅在一些特殊狀況使用var,咱們都知道在Kotlin中函數是頭等公民,而且加入不少函數式編程內容,而使用不可變的引用的變量使得更加接近函數式編程的風格。
  • 四、須要注意的是val引用的自己是不可變的,可是它指向的對象多是可變的(這個問題的討論和求證請查看Kotlin中val不可變與可讀的討論)。
  • 五、var關鍵字容許改變本身的值,可是它的類型倒是沒法改變的。

5、Kotlin中val不可變與可讀的討論

因爲Kotlin是一門新的語言,咱們在學習的過程當中常常習慣性的去記住一些所謂定理,而沒有去真正深究爲何是這樣。好比拿今天的議題來講,相信不少的人都這樣認爲(剛開始包括我本身)var修飾的變量是可變的,而val修飾的變量是不可變的。而後學完Kotlin的自定義屬性訪問器就會以爲是有問題的。而後去看一些國外的博客,雖然有講述可是看完後更讓我懵逼的是val修飾的變量的值是能夠變化的可讀的,而且底層的引用也是變化的。前面那句確實能夠理解,後面一句仍是保留意見。因而乎就開始寫demo認證。 引用國外博客的一句原話"But can we say that val guarantees that underlying reference to the object is immutable? No…" 國外博客源地址測試

  • 一、val不可變與可讀的假設

假設一: 在Kotlin中的val修飾的變量不能說不可變的,只能說val修飾變量的權限是可讀的。

假設二: 在Koltin中的val修飾的變量的引用是不可變的,可是指向的對象是可變的。

  • 二、 val不可變與可讀的論證

論證假設一: 咱們在Kotlin的開發過程當中,通常是使用了val修飾的變量就不能再次被賦值了,不然就會拋出編譯時的異常。可是不能再次被賦值不表明它是不可變的。由於Kotlin與Java不同的是多了個自定義屬性訪問器的特性。這個特性貌似就和val修飾的變量是不可變的矛盾了。而Java中不存在這個問題,若是使用了final修飾的變量,沒有所謂自定義訪問器概念。

fun main(args: Array<String>) {
    val name = "Hello Kotlin"
    name = "Hello Java"
}
複製代碼

print error:

Error:(8, 5) Kotlin: Val cannot be reassigned
複製代碼

定義get屬性訪問器例子

class RandomNum {
    val num: Int
        get() = Random().nextInt()
}

fun main(args: Array<String>) {
    println("the num is ${RandomNum().num}")
}
複製代碼

print result:

the num is -1411951962
the num is -1719429461
複製代碼

總結: 由以上的例子能夠說明假設一是成立的,在Kotlin中的val修飾的變量不能說是不可變的,而只能說僅僅具備可讀權限。

論證假設二: 由論證一,咱們知道Kotlin的val修飾的變量是可變的,那它的底層引用是不是可變的呢?國外一篇博客說引用是可變的,真是這樣嗎?經過一個例子來講明。

User類:

package com.mikyou.kotlin.valtest
open class User() {
    var name: String? = "test"
    var age: Int = 18
    var career: String? = "Student"
}
複製代碼

Student類:

class Student() : User()
複製代碼

Teacher類:

class Teacher() : User()
複製代碼

Customer接口:

interface Customer {
    val user: User//注意: 這裏是個val修飾的User實例引用
}
複製代碼

VipCustomer實現類:

class VipCustomer : Customer {
    override val user: User
        get() {
// return Student().apply {
// name = "mikyou"
// age = 18
// career = "HighStudent"
// }
            return Teacher().apply {
                //看到這裏不少人確定認爲,底層引用也會發生改變,畢竟Student, Teacher是不一樣的對象了。可是事實是這樣的嗎?
                name = "youkmi"
                age = 28
                career = "HighTeacher"
            }
        }
}
複製代碼

測試:

fun main(args: Array<String>) = VipCustomer().user.run {
    println("my name is $name, I'm $age years old, my career is $career, my unique hash code is ${hashCode()} ")
}
複製代碼

print result:

my name is mikyou, I'm 18 years old, my career is HighStudent, my unique hash code is 666988784 //切換到Teacher my name is youkmi, I'm 28 years old, my career is HighTeacher, my unique hash code is 666988784
複製代碼

總結: 由以上的例子能夠說明假設二是成立的,兩個不一樣的對象hashCode同樣說明,user的引用地址不變的,而變化的是引用指向的對象是能夠變化的。

到這裏Kotlin入門基礎語法就結束了,下一篇將會繼續深刻Kotlin相關內容

歡迎關注Kotlin開發者聯盟,這裏有最新Kotlin技術文章,每週會不按期翻譯一篇Kotlin國外技術文章。若是你也喜歡Kotlin,歡迎加入咱們~~~

相關文章
相關標籤/搜索