Kotlin 数据之本变量

变量和常量用来存储数据和传递数据,类型则用来描述变量和常量保存的是什么样的数据类型。

数据类型

变量和常量都有数据类型,数据类型用来描述变量和常量保存的数据,告诉编译器该如何进行类型检查。理论结合实践,我们创建一个新项目,项目名,VarType(变量类型)。

使用IntelliJ创建新项目

第一次打开IntelliJ集成开发环境,点击New Project


选择Kotlin,选择 JVM | IDEA,表示用Java JVM 开发Kotlin编程。


点击Next,如图


最后点击Finish,项目创建完成。


右键src,选择New,选择Kotlin File/Class


选择File,输入TypeIntro,按下回车后新文件会在编辑器中打开。

输入main函数

fun main() {
    
}

变量声明

在TypeIntro中创建一个apple变量,表示有6个苹果

fun main() {
    var apple: Int = 6  // 有6 个苹果
    println(apple)  // 打印苹果变量值到控制台
}

上述代码中,将一个Int类型值赋给了apple。var关键字定义了一个变量。var 后面跟着变量名,便是你想定义一个新变量。使用:Int 指定变量的类型,:Int变量表明apple要存储的是整数。使用赋值运算符(=) 把右边的实例值6赋值给左边的apple。 // 代表注释,会完全忽略注释

变量定义的图解


定义变量并赋值,接下来使用println函数将变量值输出到控制台。


现在,尝试将apple 变量赋值 "red"

我们看到有一个红色的波浪线,编译器说字符串实例不能赋值给Int类型。我们把鼠标放到红色的波浪线上,会有一个错误提示弹框。我们输入的每一个字符,每一行代码,都在和编译器沟通,编译器一直在做语法检查。确保每行代码都符合语法要求!就像我们学习英语的时候,语法决定了语句的正确性!

为了改正错误类型,我们将apple实例值改为5。

fun main() {
    var apple: Int = 5
    println(apple)
}

班里现在有10哥同学,5个苹果不够吃,我们在增加5个苹果。

fun main() {
    var apple: Int = 5
    apple += 5
    println(apple)
}

现在我们总共有了10个苹果,运行代码,看看结果

我们先来看看最常用的类型

类型 描述 示例
String 字符串 "Apple"
char 单字符 'X'
Int 整数 6
Boolean true/false值 true
Double 小数 3.14
List 元素集合 1,2,4
Set 无重复元素结合 "Hello" "WOrld"
Map 键值对 "apple" to 6,"banana" to "8"

只读变量

前面你已经看到,变量可以重新赋值。但通常我们也需要不能在重新赋值的变量。例如,丈夫的工资都在媳妇的银行卡里,媳妇给了丈夫每天60元生活费,除此之外丈夫身无分文,媳妇不希望丈夫有更多的钱,我们来定义一个丈夫的生活费变量。

fun main() {
    var apple: Int = 5
    apple += 5
    println(apple)
    val husbandExpense  = 60   // 媳妇定义了丈夫每天60元的生活费,媳妇不希望丈夫有更多的钱
    husbandExpense +=100     // 丈夫想要更多的钱,那是不可能地,你会发现这行代码出错了
    println(husbandExpense)
}

只读变量只能在定义时赋初值,以后不能在改它的值。我们注释掉丈夫生活费增加100元的那行代码,如下所示:

现在运行代码,看看输出结果

10
60

Process finished with exit code 0

编译器在我们输入代码时,能判定可变变量和只读变量是否运用恰当。如果一个不可变变量使用了var 关键字,编译器会提示我们改为val。我们只要有必要,都建议使用 val 关键字。

类型推断

注意看上面的代码,只读变量 husbandExpense 我们没有加变量类型,编译器使用了类型推断。对于以声明并赋值的变量,编译器清楚的知道这个变量的类型是什么。它允许你省略类型定义!
我们再注意看一开始定义的 apple 可变变量,:Int是灰色的,编译器说类型定义是多余的,对于以声明并赋值的变量,已经精确的给出了类型,这就是Kotlin的自动类型推断。
单击 apple 变量旁边的Int 类型定义,然后按下(Alt + Enter)组合键,如图

按下回车键,IntelliJ 能够自动帮你删除冗余的代码。可以看到 Int 类型定义不见了。我们以后编写代码都不会明确指定变量的类型,除非有歧义,这样的代码编写的更简洁!

我们按下 Ctrl + Shift + P 组合键,IntelliJ 会自动显示出变量的类型。

编译时常量

编译时常量只能在函数之外定义,只是因为编译时常量,只能在编译时赋值,而main函数和其它函数都是在运行时才调用。函数内的变量也是在那是赋值的。
因为使用复杂的数据类型可能会危害编译时的安全保障,所以编译时常量只能是一些常见的基本数据类型。基本数据类型我们很快就会全部学完。
使用const 修饰符定义一个编译时常量

//媳妇很坚定的说,你每天最多只有60元生活费,而且这辈子我说了算
const val MAX_EXPENSE: Int = 60
fun main() {
  
}

MAX_EXPENSE 由 const 和 val 修饰符一起告诉编译器,MAX_EXPENSE 常量值绝不会改变。这也就是说,整数值60要绝对保证不变。编译器就知道如何灵活处理代码。

对 MAX_EXPENSE 的书写格式感到好奇吗?没错,为了凸显编译时常量,我们都是这么写的。

前面不是有只读变量吗,为什么要加编译时常量呢!因为只读变量不能保证变量一定不能修改。

常量Kotlin 字节码,看下图

熟悉Java 的朋友阅读起来很熟悉,不熟悉Java的朋友做个参数

import kotlin.Metadata;

@Metadata(
   mv = {1, 1, 16},
   bv = {1, 0, 3},
   k = 2,
   d1 = {"\u0000\u000e\n\u0000\n\u0002\u0010\b\n\u0000\n\u0002\u0010\u0002\n\u0000\u001a\u0006\u0010\u0002\u001a\u00020\u0003\"\u000e\u0010\u0000\u001a\u00020\u0001X\u0086T¢\u0006\u0002\n\u0000¨\u0006\u0004"},
   d2 = {"MAX_EXPENSE", "", "main", "", "VarType"}
)
public final class TypeIntroKt {
   public static final int MAX_EXPENSE = 60;

   public static final void main() {
      int apple = 5;
      int apple = apple + 5;
      boolean var1 = false;
      System.out.println(apple);
      int husbandExpense = 60;
      boolean var2 = false;
      System.out.println(husbandExpense);
   }

   // $FF: synthetic method
   public static void main(String[] var0) {
      main();
   }
}

我们发现Kotlin源码中定义的 Int 引用数据类型,翻译成Java源码后是基本数据类型。Kotlin 没有基本数据类型,Kotlin 编译器会根据情况,将引用数据类型编译成基本数据类型,以提高代码的性能。Kotlin在背后为我们做了许多事情,背后的故事我们来看两个变量。注意观察 apple 变量 和 husbandExpense 变量的定义:

    var apple = 5
    val husbandExpense = 60

在Kotlin 源码中,我们已经省略了变量类型的定义,但在字节码中,我们反编译字节码,得到Java代码,还是看到它们的显示类型定义。由此可见,字节码讲述了背后的故事,即Kotlin支持类型推断,所以不像Java那样,需要显示指定变量类型定义。

我们在这里学习了可变变量和只读变量存储基本数据,知道何时改用哪种变量。这主要取决于变量值是否能修改;了解了如何使用编译时常量定义不可变变量;学会了使用Kotlin自动类型推断,在声明时简化代码书写。后面我们会反复用到这些知识。下一章我们将学习条件语句,写一个简单的计算器。