从Java入门Kotlin-Kotlin类

​ 介于是Kotlin系列的第一篇文章,按照惯例应该先吹一波Kotlin怎么怎么好,我觉着这个事情还是等教程结束再来谈,到时候会单独出一篇文章,现在只知道是Better Java就可以了。

​ 我是因为工作原因—公司项目大部分是Kotlin,才接触然后学习的Kotlin,我觉着现在提起Kotlin大家的第一反应应该是安卓开发,确实Kotlin在安卓领域占有绝对的优势,但对于我们服务端来说Kotlin占的份额还是很小的,像我们公司服务端全转换用Kotlin的就更少之又少了,经过一段时间的学习和使用,逐渐觉着Kotlin已经有能力担任我们服务端的开发,并且在我们公司已经正常稳定运行了很久了,代码也相比之前优雅了许多,所以我也想为Kotlin的普及做出一小份力,从一个从前Java服务端出发,对比Java和Kotlin的异同,来入门这款优秀的语言。介于我的Java水平和Kotlin水平都有限,文章有错误或不对的地方希望大家不吝指教,我们一块探讨。

类声明

Java和Kotlin单从类声明上说从表面上看上去是没有什么差别的

Java:

1
2
3
public class AnimalJava {

}

Kotlin:

1
2
3
class AnimalKotlin {

}

但其实他们之间是有不小的区别的:Java允许创建任意类的子类并重写任意方法,除非显式的去定义成_final_,这其实是容易造成问题的,基类的代码修改容易使其不符合在子类中的假设。所以Kotlin中为了避免这个问题的发生,Kotlin中的类默认都是_final_的,如果允许被继承,则需要使用_open_这个修饰符。

需要注意的是:在Spring中全部定义为final是不方便的,因为这样的话,Spring的代理就不好使了,所以在Spring中使用Kotlin的时候我们一般会使用一个叫All-Open的插件,它会自动为Spring使用open类的地方加上open,而不用代码中写很多open

类成员变量

了解先来看下Java和Kotlin类中成员变量的定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class AnimalJava {
String name;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}

class AnimalKotlin {
var name: String = "" // 或者 val name: String = ""
}

其中Java中的name默认的是private,如果外部需要访问这个成员变量是需要显式调用name的get和set方法的,而Kotlin中默认的是public,需要一个默认值,并且Kotlin中区分了val和var,外部访问和赋值是不需要显式调用get和set方法。

不知道大家有没有对Java中成员变量设成private,然后提供get和set方法有过好奇,我查着大概说是为了风中的特性来将数据与行为进行分离。而Kotlin中则直接是一个public的成员变量,岂不是直接破坏了Java中的封装的特性,其实这里Kotlin并没有:Kotlin中也是可以使用get和set的,当我们不定义的话相当于使用默认的get和set,我们也可以显式自定义get和set,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
class AnimalKotlin {
var name: String = ""
//不写的话默认是这个样子的,其中field是一个幕后字段代表着name
set(value) { field = value }
get() {return field}
}

fun main() {
val animal = AnimalKotlin()
//这里看似是直接赋值,其实是调用了set方法
animal.name = "dog"
print(animal.name)
}

看完上面我说的是不是感觉好像是有什么黑魔法一样,全是我自己根据表象进行猜测的,口说无凭,我们直接把看一下上面代码的字节码转成Java之后的代码是什么样子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public final class AnimalKotlin {
@NotNull
private String name = "";

@NotNull
public final String getName() {
return this.name;
}

public final void setName(@NotNull String value) {
Intrinsics.checkParameterIsNotNull(value, "value");
this.name = value;
}
}

public final class AnimalKotlinKt {
public static final void main() {
AnimalKotlin animal = new AnimalKotlin();
animal.setName("dog");
String var1 = animal.getName();
boolean var2 = false;
System.out.print(var1);
}

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

这下是不是全都明白了,其实跟Java是一样的,只是当我们使用的时候方便了许多,Kotlin去除了很多模板代码,使代码写起来更方便,如果我想让set或者get私有只需要下面这样处理就可以:

1
2
3
4
class AnimalKotlin {
var name: String = ""
private set
}

这样set方法就是私有了,此时*animal.name = “dog”*这个是会报错的因为其实就是调用了set方法。

当我们使用的不是var而是val的时候,此时只会有get没有set

拓展:成员变量中对null的处理,Kotlin为了解决Java中null的困扰,从源头控制,比如我们例子中*var name: String = “”,这样name无论如何都不会为null,当我们使用var name: String? = “”*的时候此时name就有可能为null,我们用这个属性的时候就要进行null判断,后面我们会详细介绍null的处理

讲到这里我们会发现,每个类的属性都会有默认值,我们不想设置默认值怎么办,这里有两个方法:

其一:作为主构造函数参数(下面要讲),

其二:使用lateinit,他的意思是延迟初始化,如下:

1
2
3
class AnimalKotlin {
lateinit var name: String
}

这种使用最多的场景是在spring中的@Autowire,这个还是要记住的,以后会常用

如上关于类中的属性基本就介绍完了,还有一个知识点是委托属性,这个以后再说,刚开始讲不太好接受

类的构造

1
2
3
4
5
6
7
8
9
public class AnimalJava {
String name;

public AnimalJava(){
}
public AnimalJava(String name){
this.name = name;
}
}

Java中的类构造如上面所示,其中包含了一个无参构造方法和一个有参构造方法