内容简介
前面讲了 Kotlin 是如何定义一个类,我们发现与 Java 还是存在很多不同。今天我们来讲解下 Kotlin 中都有什么样的类,以及如何定义呢?
讲解前大家想想 Java 中有什么样的类呢?普通类丶抽象类丶非静态内部类丶静态内部类丶匿名内部类丶枚举类,我想大家应该对这些类都在熟悉不过了吧?Kotlin 中也存在这些类只是定义方式不同罢了,并且 Kotlin 中新增了部分类(密封类 丶 Data类丶单例类)。
静态内部类
静态内部类,定义在一个类里面,主要特点就是不持有外部类的引用。 Kotlin
中如何定义呢?
class Parent{
/**
* 定义静态内部类
*/
class Child{}
}
反编译的代码:
public final class Parent {
...
public static final class Child {
}
}
我们看看反编译的 Java
代码,可以发现 Kotlin
中默认定义的内部类就是静态内部类(刚好与 Java
相反)。还发现定义的静态内部类是被 final
修饰的,也就是说不可以被继承的,那如何让其可以继承呢?通过 open
修饰即可啦。
非静态内部类
非静态内部类,持有外部类的引用(小心内存泄漏哦)。 Kotlin
中只需要通过 inner
修饰符,修饰内部类即可。
class Parent{
val value = "我是外部的变量"
/**
* 定义非静态内部类
*/
inner class Child{
fun call(){
println(value)
}
}
}
fun main() {
// 定义非静态内部类,必须先创建外层类的一个对象,再创建内部类
val child = Parent().Child()
child.call()
}
输出结果:
我是外部的变量
反编译的代码:
public final class Parent {
@NotNull
private final String value = "我是父类的变量";
@NotNull
public final String getValue() {
return this.value;
}
public final class Child {
public final void call() {
String var1 = Parent.this.getValue();
boolean var2 = false;
System.out.println(var1);
}
}
}
通过上面的 Java
的代码可以清晰的看出内部类,没有被 static
修饰。
匿名内部类
匿名内部类,在 Android
中经常被使用,例如我们经常一个方法的参数接收一个接口的类型或者抽象类对象,我们以前都会直接通过 new
关键词,直接创建一个无名类的对象,并实现其对应方法。
其实匿名内部类,并不是没有名字的。在编译后也会生成 class
文件,类名会定义成 外部类昵称$N
(这里的N是一个数字,数字依次递增表示多个匿名内部类)。
/**
* 定义抽象类
*/
abstract class AbstractClass(val name:String){
abstract fun call2()
}
class Parent{
/**
* 定义一个匿名内部类,赋值给变量
*/
val callImpl = object : ICall {
override fun call() {
println("实现")
}
}
/**
* 定义抽象类对应的匿名内部类
*/
val callE=object :AbstractClass("阿文"){
override fun call2() {
println("继承")
}
}
}
我们看到 Kotlin
中定义匿名内部类不再是通过 new
关键词来定义了?而是使用 object
来定义,并且若抽象类具有构造函数,一定要传入对应构造参数。(大家想想 object 关键词还在那里用到过呢?)
我们来看看生成的 class
文件。
的确是生成了2个 class
文件,奇怪怎么和我说的 外部类昵称$N
结果不一样呢?我特意在写了一个 Java
类。
public class Test {
ICall callImpl = new ICall() {
@Override
public void call() {
}
};
AbstractClass call2 = new AbstractClass("阿文") {
@Override
public void call2() {
}
};
}
接下来,我通过 ApkTool
反编译了下,直接看 smali
文件更为的直接。看下图 Java
的确是这样命名规则,但是 Kotlin
规则变了 外部类昵称$接收对象昵称$N
(应该是 Kotlin
编译器搞的鬼吧)。
为何纠结这个问题呢?因为有时候我想反射创建匿名内部类对象的时候,会用到这个知识点。
枚举类
枚举类和 Java
是一样的,并且支持使用 when
语句。
enum class TestEnum {
A, B, C
}
fun main() {
val value = TestEnum.A
/**
* 发现有啥特点吗? when 若包含枚举的全部类型,不用写 else 就能使用返回值功能了
*/
val type = when (value) {
TestEnum.A -> {
0
}
TestEnum.B -> {
1
}
TestEnum.C -> {
2
}
}
}
单例类
在 Java
中单例设计模式是常见的一种设计模式了,常见的单例有 懒汉丶饿汉丶枚举单例丶静态内部类单例,以前写单例这种搬砖代码,我都是添加一个代码模板。 Kotlin
语言开发者似乎也头疼这个问题,为我们专门定义了一种单例类。 Kotlin
中只需要通过 object
修饰类即可。
/**
* 定义一个单例
*/
object Singleton{
fun call(){}
}
是不是超级简单?那它属于什么样的单例呢?我们看下反编译后的 Java
代码,原来是饿汉式单例。大家试想下 Kotlin
如何定义一个懒汉式的单例呢?(提示:伴生对象
public final class Singleton {
public static final Singleton INSTANCE;
public final void call() {
}
private Singleton() {
}
static {
Singleton var0 = new Singleton();
INSTANCE = var0;
}
}
data 类
在 Java
中是不是经常定义实体对象呀,然后重写其 get
丶 set
丶 toString
丶 hashCode
丶 equals
等方法。 Kotlin
再也不用这么复杂了,只需要通过 data
修饰类即可。
/**
* 这样定义的类 toString,equals,hashcode 等方法都被自动写了.其中包含了 componentN 方法(这个有啥作用呢?)
*/
data class Play(val name: String, val time: Int)
fun main() {
val play = Play("阿文", 10000)
// componentN
val (name, time) = play
println("$name $time")
}
看下反编译结果:
public final class Play {
@NotNull
private final String name;
private final int time;
@NotNull
public final String getName() {
return this.name;
}
public final int getTime() {
return this.time;
}
public Play(@NotNull String name, int time) {
...
}
@NotNull
public final String component1() {
return this.name;
}
public final int component2() {
return this.time;
}
@NotNull
public final Play copy(@NotNull String name, int time) {
...
}
// $FF: synthetic method
@NotNull
public static Play copy$default(Play var0, String var1, int var2, int var3, Object var4) {
...
}
@NotNull
public String toString() {
return "Play(name=" + this.name + ", time=" + this.time + ")";
}
public int hashCode() {
String var10000 = this.name;
return (var10000 != null ? var10000.hashCode() : 0) * 31 + this.time;
}
public boolean equals(@Nullable Object var1) {
....
}
}
通过上面的反编译代码,发现还是重写了很多方法,并且定义了 copy
方法。(深拷贝呢?还是浅拷贝呢?
这里补充下
componentN
方法,这个类似一个操作符重载,后续会讲到。其实在测试用例中,我演示过用法val(name,time)=play
快速创建了2个name
和time
变量,并且将play
对象中的component1
和component2
方法返回值将其赋值。这个是不是很眼熟呀?没错集合遍历的时候用到这个点了。
fun main() {
val mutableMapOf = mutableMapOf<String, Int>("阿文" to 18, "小丽" to 18)
// 是不是很眼熟呀
for ((value, key) in mutableMapOf) {
}
}
密封类
密封类定义 sealed
修饰,密封类定义有限个数的子类,并且限制定义范围(不包含间接继承,和 Java
中的枚举有异曲同工之处。
密封类与枚举类存在本质区别,密封类是注重子类个数,枚举类注重的是有限对象个数。那密封类有什么好处呢?举个例子:用户场景,分为3种用户 普通会员丶 会员丶 超级会员,我们每种会员都有对应的特殊功能,所有的会员都要是 User
的子类。以前我们可以通过枚举定义这种会员类型,但是不能对其定制特定的方法与属性,在 Kotlin
中可以用密封类来代替。
/**
* 定义密封类使用 sealed 关键词,来尝试完成Java的枚举类
*/
sealed class User(val nameCore: String) {
object VIP : User("会员")
object SUPER_VIP : User("超级会员") {
fun getUserPhone(): String {
return "我是超级会员的特有方法"
}
}
object COMMON_VIP : User("普通会员")
}
/**
* 在 `Kotin 1.1` 修改了密封类的继承范围,在同一个kt文件都可以继承。
*/
open class CommonUser : User("普通用户")
fun main() {
// 密封类可以配合 when 语法
val user: User = User.SUPER_VIP
when (user) {
User.VIP -> {
}
User.COMMON_VIP -> {
}
User.SUPER_VIP -> {
}
}
}
补充下:在
Kotin1.1
修改了密封类的继承范围,只需要在同一个 kt 文件都可以继承。sealed
修饰的类为何不能new
呢?其实Kotlin
编译器搞的鬼,将密封类编译成一个抽象类,所以无法直接创建对象。
类的映射 typealias
这是个补充点,为何放在类篇章呢?因为它和类有一定关系。还记得集合篇章我说过 Kotlin
并没有重复造轮子,完全使用的 Java
中的集合。怎么实现的呢?其实就是通过 typealias
关键词。
/**
* 定义类的映射
*/
typealias KotlinFile = File
fun main() {
/**
* 使用和File的功能一样
* Kotlin在编译器会自动替换
*/
val file:File = KotlinFile("ahah")
file.exists()
}
其实 typealias
关键词主要在告诉编译器,编译到我定义的 KotlinFile
请给我替换成 File
。
接下来看看,
Kotlin
中定义的集合映射吧!大伙想没想过为啥要这样做呢?个人理解为了解耦,假如有一天Kotlin
想造轮子了,为了保证以前的代码不用更改,只需要修改映射关系即可。
@SinceKotlin("1.1") public actual typealias RandomAccess = Java.util.RandomAccess
@SinceKotlin("1.1") public actual typealias ArrayList<E> = Java.util.ArrayList<E>
@SinceKotlin("1.1") public actual typealias LinkedHashMap<K, V> = Java.util.LinkedHashMap<K, V>
@SinceKotlin("1.1") public actual typealias HashMap<K, V> = Java.util.HashMap<K, V>
@SinceKotlin("1.1") public actual typealias LinkedHashSet<E> = Java.util.LinkedHashSet<E>
@SinceKotlin("1.1") public actual typealias HashSet<E> = Java.util.HashSet<E>
推荐阅读
--END--
识别二维码,关注我们