Kotlin 真香之密封类

不知道大家,有没有在 Java 中做过这样的事情,申明一个枚举,在枚举中定义各种值,其中他们各自在构造函数中做不同的初始化工作。

public enum Color {
    Red("I'm Red"),
    Yellow("I'm Yellow"),
    Blue("I'm Blue");

    private final String desc;

    Color(String desc) {
        this.desc = desc;
    }

    public static void main(String[] args) {
        System.out.println(Color.Blue.desc);
    }
}

这样的好处在于,限定死了 Color 的种类,在具体使用时直接用就好,Enum 这种方式,有个最大的难题在于,所以不能控制对象的构建时机,当类构建时 Color 中的各种子类也必须构建好。另一方面,如果通过类继承的方式来做的话,因为无法限制范围,你想实现多少个就多少个,代码可能需要用到 instanceof 这个关键字,

Java 老师告诉我们一般出现这个关键字,大概率代码中出现了异味

Usage

kotlin 语言的研发者,也发现了这个问题,于是给我们封装一个语法糖 sealed ,中文学名也就密封类。密封类结合了两者的优点,同时避免了两者的缺点。

我们来看看具体的例子,特别简单。

sealed class Fruit

class Apple: Fruit()

class Banana: Fruit()

注意三者都在一个文件中。将一个类申明为 sealed 之后,只能在同样的文件中定义其子类,在其他地方无法构建其子类,也就是说 Fruit 的子类,被完全限定在这个文件中了。

class Pear: Fruit()

如果我们尝试在另一个文件中,继承 Fruit 实现另一个类的话,会编译不过,提示构造函数 not accessable。这样就帮助我们即限定了范围,又不影响我们的构造时机,实乃天赐良方呀。

Under in hood

还是和上一篇文章一样,我们通过反编译的方式来看看,背后的原理是怎样的,会不会同样大吃一惊。

// Fruit.java
package com.tqs.android.kotlin.kotlin;

import kotlin.Metadata;
import kotlin.jvm.internal.DefaultConstructorMarker;
public abstract class Fruit {
   private Fruit() {
   }
   // $FF: synthetic method
   public Fruit(DefaultConstructorMarker $constructor_marker) {
      this();
   }
}

// Banana.java
package com.tqs.android.kotlin.kotlin;
import kotlin.Metadata;
import kotlin.jvm.internal.DefaultConstructorMarker;
public final class Banana extends Fruit {
   public Banana() {
      super((DefaultConstructorMarker)null);
   }
}

// Apple.java
package com.tqs.android.kotlin.kotlin;

import kotlin.Metadata;
import kotlin.jvm.internal.DefaultConstructorMarker;
public final class Apple extends Fruit {
   public Apple() {
      super((DefaultConstructorMarker)null);
   }
}

我们看到对于 sealed 修饰的类,实现一个私有的构造函数,同时添加了一个 public 的构造函数,里面有一个 DefaultConstructorMarker 的参数。咦?那我们岂不是可以在外界通过这个 public 的构造函数来申明新的对象?这个地方就是 kotlin 编译器为我们做的限制,DefaultConstructorMarker 是 kotlin internal 的,kotlin 限制 internal 中对象不能被外部访问。

这就是 sealed class 的秘密!

额外,留一个问题,如果 Fruit 类构造函数里面,有一个 参数,情况会有所不同吗?原理是什么?

我来评几句
登录后评论

已发表评论数()

相关站点

热门文章