轻松掌握Kotlin的基本要素

在上一篇文章中,我们初步了解了什么是Kotlin,重点关注其与Java的互操作性,了解了Kotlin的主要特征以及在Android Studio中如何将Java代码转换为Kotlin代码。如果您还没有阅读,可以先阅读后再进行下一步学习。本文主要介绍Kotlin的基本要素:变量、函数和类以及属性首先我们先来学习函数和变量。

1、基本要素:函数和变量

现在,我们就从最最最经典的例子开始:打印一个“Hello world!”。代码如下:

@Test
fun main(args:Array<String>){
    println("Hello world!")
}

从上面的代码中,我们可以观察到:

  • 使用关键字fun声明一个函数
  • 参数的类型写在它的名称后面,变量的声明也一样。
  • 函数可以定义在文件的最外层不需要把它放在类中
  • 数组就是类。和Java不同,Kotlin没有声明数组类型的特殊语法。
  • 使用print()/println()来进行输出。
  • 可以省略每行代码结尾的分号

是不是看起来很爽,那么接下来让我们看下函数声明的语法。

1.1 函数

//Kotlin函数声明:
//fun 函数名(参数列表):返回类型{函数体}
fun max(a:Int,b:Int):Int{
    return if (a > b) a else b
}

在上面的例子中,我们使用关键字fun声明了一个max函数用来比较两个数值的大小,并返回数值大的那一个,可以看出返回值是跟在参数列表后的,并用一个冒号隔开。注意在Kotlin中,if是有结果值的表达式,它和Java中的三元运算符相似:(a>b)?a:b

表达式函数体(很重要)
我们可以来优化上面的代码。因为它的函数体是由单个表达式构成的,可以用这个表达式作为完整的函数体,并去掉花括号和return语句:

fun max(a:Int,b:Int):Int 
        = if (a > b) a else b

如果函数体写在花括号中,这个函数有代码块体。如果它直接返回一个表达式,则其有表达式体

甚至,我们还可以将返回类型给省掉,如下:

fun max(a:Int,b:Int)
        = if (a > b) a else b

对表达式体函数来说,编译器会分析作为函数体的表达式,并把它的类型作为函数的返回类型,即使没有显式地写出来,这就是类型推导

注意:只有表达式体函数的返回类型可以省略。对于有返回值的代码块体函数,必须显式地写出返回类型和return语句。

1.2 变量

在Kotlin中,变量以关键字开始,然后是变量名称,最后可以加上类型(可不加),例如:

val str:String = "Hello world!"
var a = 1 //类型可以被推导出来

如果变量没有初始化器,需要显式地指定它的类型:

val b:String
b = str

如果不能提供可以赋给变量的值的信息,编译器就无法推断出它的类型。

可变变量和不可变量

  1. val:不可变引用。使用val声明的变量不能在初始化之后再次赋值,它对应的是Java的final变量。
  2. var:可变引用。这种变量的值可以被改变,对应的是普通(非final)的Java变量。

默认情况下,应该尽可能地使用val关键字来声明所有的Kotlin变量,仅在必要的时候换成var。使用不可变引用、不可变对象及无副作用的函数让代码更接近函数式编程风格

在定义了val变量的代码块执行期间,val变量只能进行唯一一次初始化。但是,如果编译器能确保只有唯一一条初始化语句会被执行,可以根据条件使用不同的值来初始化它,如下代码:

val message:String
if (condition){
    message = "true"
}else {
    message = "false"
}

注意,尽管val引用自身是不可变的,但是它指向的对象可能是可变的,如下代码:

//声明不可变引用
val languages = arrayListOf("Java")
//改变引用指向的对象
languages.add("Kotlin")

var关键字允许变量改变自己的值,但它的类型却是改变不了的,如下代码:

var a = 1
a = "heidou"//报错,类型不匹配
//可以使用"xx".toInt()来进行强转

1.3 字符串模板

Kotlin允许在字符串字面值中引用局部变量,只需要在变量名称前面加上字符$,如下代码:

val str = "heidou"
println("我的名字叫$str")
//使用复杂表达式需要使用花括号{}括起来
println(println("1 + 3 = ${1+3}"))

2、类和属性

在Kotlin中,我们可以用更少的代码来完成许多常见的任务,例如定义一个学生类JavaBean,代码如下:

public class Student {
    private String name;
    
    public Student(String name) {
        this.name = name;
    }
    
    public String getName() {
        return name;
    }
}

Student类中只有一个name属性。在Java中,构造方法的方法体常常包含完全重复的代码:它把参数赋值给有着相同名称的字段。在Kotlin中,这种逻辑不用这么多的样板代码就可以表达。在Android Studio中点击“Code"–>"Convert Java File to Kotlin File”将上面的Java类转换为Kotlin类,代码如下:

//这种类(只有数据没有其它代码)通常被叫作值对象
class Student(var name: String)

是不是感觉很简洁,我们再也不用编写过多的重复代码了,美滋滋。
注意:从Java到Kotlin的转换过程中public修饰符不见了。在Kotlin中,public是默认的可见性,所以可以省略。在后续文章中我们会对Kotlin的可见性进行详细讲解。

2.1 属性

类的概念就是把数据和处理数据的代码封装成一个单一的实体。在Java中,数据存储在字段中,通常还是私有的。我们会提供访问器方法getter和setter用来让类的使用者访问到数据。例如上面Student类,在getter和setter中我们还可以添加额外的逻辑。
在Java中,字段和其访问器的组合常常被叫作属性。在Kotlin中,属性是头等的语言特性,完全代替了字段和访问器方法。在类中声明一个属性和声明一个变量一样:使用val和var关键字。声明成val的属性是只读的,而var属性是可变的。

class Student(
        //可写属性:生成一个字段、一个getter和一个setter
        var name: String,
        //只读属性:生成一个字段和一个getter
        val score:Int
)

当我们声明属性的时候,就已经声明了对应的访问器(getter、setter)。访问器的默认实现非常简单:创建一个存储值的字段,以及返回值的getter和更新值的setter。有必要的时候,我们也可以自定义我们想要的访问器,自定义访问器在下面会进行介绍。
如何在Java中使用Kotlin定义的类。Kotlin Student类声明隐藏了和原始Java代码相同的底层实现:它是一个字段都是私有的类,这些字段在构造方法中初始化并能够通过对应的getter、setter访问。代码如下:

@Test
public void main() {
    Student stu = new Student("Heidou", 100);
    System.out.println("name = " + stu.getName() 
    + " , score = " + stu.getScore());
}
//输出:name = Heidou , score = 100

注意:如果属性名称是以is开头,getter不会增加任何的前缀,而setter名称中is会被替换成set。
在Kotlin中使用,代码如下:

val stu = Student("Heidou",100)
stu.name = "Hei_dou"
println("name = ${stu.name} , score = ${stu.score}")
//输出:name = Hei_dou , score = 100

可以看到,使用kotlin可以直接引用属性,不再需要调用setter和getter,是不是很方便呢?接下来我们来看看怎么进行自定义访问器。
额外知识点:对于那些在Java中定义的类,一样可以使用Kotlin的属性语法。Java类中的getter可以被当成val属性在Kotlin中访问,而一对getter/setter可以被当成var属性访问。

2.2 自定义访问器

我们来声明一个矩形,它能够判断自己是否是正方形。不需要一个单独的字段来存储这个信息(是否是正方形),因为可以随时通过检查矩形的长度是否相等来判断,代码如下:

class Rectangle(val height: Int, val width: Int) {
    val isSquare: Boolean
        //声明属性的getter
        get() = height == width
}val rectangle = Rectangle(200,200)
println(rectangle.isSquare) //输出true

属性isSquare不需要字段来保存它的值。它只有一个自定义实现的getter,它的值是每次访问属性的时候计算出来的。
再看看下面的例子,我们可以同时编写getter和setter访问器,代码如下:

class Person{
    var age:Int = 0
        set(value){
            field = value + 1
        }
        get() = field - 1
}
//field关键字代表的是当前域

2.3 Kotlin源码布局:目录和包

每一个Kotlin文件都以一条package语句开头,而文件中定义的所有声明(类、函数及属性)都会被放到这个包中。如果其它文件中定义的声明也有相同的包,这个文件可以直接使用它们:如果包不相同,则需要用import关键字导入它们。例如:

package com.heidou.xxx
import org.junit.Test

Kotlin不区分导入的是类还是函数,而且,它允许使用import关键字导入任何种类的声明。可以直接导入顶层函数的名称。可以在包名称后加上.*来导入特定包中定义的所有声明。注意这种星号导入不仅让包中定义的类可见,也会让顶层函数和属性可见。
注意:在Kotlin中,可以把多个类放在同一个文件中,文件的名称还可以随意选择。Kotlin也没有对磁盘上源文件的布局强加任何限制。如下所示,包层级结构不需要遵循目录层级结构。
在这里插入图片描述

会生成包second.SecondTest以及second.Student。
使用技巧:建议把多个类放在同一个文件中,特别是那些很小的类

最后,我们来拓展一下,看看语句和表达式的区别
语句和表达式的区别在于,表达式有值,并且能作为另一个表达式的一部分使用;而语句总是包围着它的代码块中的顶层元素,并且没有自己的值。在Java中,所有控制结构都是语句,而在Kotlin中,除了循环(for、while和do-while)以外大多数控制结构都是表达式。
另一方面,Java中的赋值操作是表达式,在Kotlin中则变成了语句,这有助于避免比较和赋值之间的混淆。

借鉴资料:《Kotlin实战》

有兴趣的读者可以关注我的微信公众号,萌新一个,希望大家多多支持。我会在接下来的每个星期(至少3篇)不定期的把自己学习内容笔记同步到微信公众号。
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值