Kotlin从入门到放弃 第七节 数据类与单例类
在一个规范的系统架构中,数据类通常占据着非常重要的角色,他们用于将服务器端或数据库中的数据映射到内存当中,为编程逻辑提供数据模型的支持。或许你听说过MVC,MVP,MVVM之类的架构模式,不管是哪一种架构模式,其中的M指的就是数据类。
数据类通常需要重写equals()、hashCode()、toString() 这几个方法。其中,equals() 方法用于判断两个数据类是否相等。hashCode() 方法作为equals() 的配套方法,也需要一起重写,否则会导致HashMap、HashSet等hash相关的系统类无法正常工作。toString() 方法用于提供更清晰的输出日志,否则一个数据类默认打印出来的就是一行内存地址。
这里我们创建一个手机数据类,字段就简单一点,只有品牌和价格。如果用Java来实现这样一个数据类,代码就需要这样写:
public class Cellphone {
String brand;
double price;
public Cellphone(String brand, double price) {
this.brand = brand;
this.price = price;
}
@Override
public boolean equals(Object object) {
if (object instanceof Cellphone) {
Cellphone other = (Cellphone)object;
return other.brand.equals(brand) && other.price.equals(price);
}
return flase;
}
@Override
public int hashCode() {
return brand.hashCode() + (int) price;
}
@Override
public String toString() {
return "Cellphone(brand = " + brand + ", price = " + price + ")";
}
}
看起来还是很复杂的,关键是这些代码还是一些没有实际逻辑意义的代码,只是为了让他拥有数据类的功能而已。而同样的功能使用Kotlin来实现就会变得非常简单。只需要在类的前面加上关键字data就可以了,就像我们前面提高让这个类变得可以被继承的话需要在类的前面加上关键字open一样。
data class Cellphone(val beand: String, val price: Double)
你没看错只需要在class前面加上data关键字,就表明你希望这个类是一个数据类,Kotlin会根据主构造函数中的参数帮我们将equals()、hashCode()、toString() 等固定且无实际逻辑意义的方法自动生产,从而大大减少了开发的工作量。
另外,当一个类中没有任何代码的时候,还可以将尾部的大括号省略。
然后我们来测试一下这个数据类,在main() 函数中编写代码:
fun main() {
val cellphone1 = Cellphone("OPPO", 2999.99)
val cellphone2 = Cellphone("vivo", 2999.99)
println(cellphone1)
priintln("cellphone1 equals cellphone2 " + (cellphone1 == cellphone2))
}
这里我们创建两个Cellphone对象,首先直接将第一个对象打印出来,然后判断这两个对象是否相等。
接下来我们在看另外一个Kotlin中特有的功能————单例类。
大家一定都听说过单例模式的,这是最常用、最基础的设计模式之一,它可以用于避免创建重复的对象。比如我们希望某个类在全局最多只能拥有一个实例,这个时候就可以使用单例模式,当然单例模式也有很多写法,这里只展示一种最常见的Java写法:
public class Singleton {
private static Singleton instance;
private Singleton() {}
public synchronized static Singleton getInstance() {
if (instance == null) instance = new Singleton;
return instance;
}
public void singletonTest() {
System.out.println("singlertonTest is called.");
}
}
这段代码其实很好理解,首先为了禁止外部创建Singleton的实例,我们需要用private关键字将Singleton的构造函数私有化,然后给外部提供了一个getInstance() 静态方法用于获取Singleton的实例。在getInstance() 方法中,我们判断如果当前缓存的Singleton实例为null,就创建一个新的实例,否则直接返回缓存的实例即可,这就是简单的单例模式的工作机制。
而如果我们想调用单例类中的方法,也很简单,比如想调用上述的singlietonTest() 方法,就可以这样写:
Singleton singleton = Singleton.getInstance();
singleton.singletonTest();
虽然Java中的单列并不复杂,但是Kotlin中明显做的更好,它同样是将一些固定的、重复的逻辑实现隐藏了起来,只暴露给我们最简单方便的用法。
在Kotlin中创建一个单例类的方式极其简单,只需要将class关键字改成object关键字即可。现在我们尝试创建一个Kotlin版的Singleton单例类,右击com.example.helloworld包 → New → Koltin File/Class,在弹出的对话框中输入"Singleton",创建类型选择Object,点击OK完成创建,初始代码如下:
object singleton{
}
现在singleton就是一个单例类了,我们可以直接在这个类中编写需要的函数,比如加入一个singletonTest()函数
object singleton{
fun singletonTest() {
println("singletonTest is called.")
}
}
可以看到,在Kotlin中我们不需要私有化构造函数,也不需要提供getInstance()这样的静态方法,只需要把class关键字改成object关键字,一个单列类就创建完成了。而调用单例类中的函数也很简单,比较类似于Java中静态方法的调用方式:
Singleton.singletonTest()
这种写法虽然看上去像是静态方法的调用,但其实Kotlin在背后自动帮我们创建了一个Singleton类的实例,并且保证全局只会存在一个Singleton实例。
到此为止,Kotlin面向对象最主要的知识就结束了。这些都是日后编程的基础,希望你能好好消化。