浅谈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,欢迎加入咱们~~~