Android技术知识Android进阶之路Android开发

Kotlin 密封类进化了

2021-06-30  本文已影响0人  hi_dhl

这是 Sealed Classes(密封类)系列的第三篇文章,之前的文章从原理、优化、实战以不同的角度分别介绍了 Sealed Classes 的强大。

Kotlin Sealed 是什么?为什么 Google 都在用 文章中主要包含以下内容:

Kotlin 中的密封类 优于 带标签的类 文章中主要包含以下内容:

而这篇文章主要来介绍 Sealed Classes 在 Kotlin 1.5.0 上带来的新特性及原理分析,在开始分析之前,先来简单的回顾一下之前文章的内容。

Sealed Classes

什么是 Sealed Classes?

Sealed Classes 用于表示受限制的类层次结构,其实这句话可以拆成两句话来理解。

Sealed Classes 的优点:

这里只是简单的回顾了一下之前的内容,接下来我们一起来看看,在新版本 Kotlin 1.5.0 中 Sealed Classes 给我们带来了那些优化。

Sealed Classes 进化了

在 2021 年 5 月份,Kotlin 官方发布了 1.5.0,在这个版本中带来几个非常有用的特性,而在这篇文章我们主要介绍 Sealed Classes,更多信息请查看 What's new in Kotlin 1.5.0

在 Kotlin 1.0 时,子类只能在 Sealed Classes 内部中使用,因为 Sealed class 会被编译成 abstract class,并且默认的构造方法被私有化了,所以子类必须嵌套在 Sealed Classes 类中。

在 Kotlin 1.1 时,允许顶级的 Sealed Classes 和它顶级子类在同一个文件中,因为编译器会自动生成了一个 公有 的构造方法,在子类的构造方法中调用了父类 公有 的构造方法,而这些都是 Kotlin 编译器帮我们做的。

在 Kotlin 1.5.0 中,放宽了对 Sealed Classes 限制,只需要保证 Sealed Classes 和它的子类,在同一个包名和 module 下面即可,这些都是 Kotlin 编译器帮我们做的。

接下里我们一起来分析一下为什么 Kotlin 需要升级 Sealed Classes?在之前的文章 Kotlin 中的密封类 优于 带标签的类 分析了 Sealed Classes 有很多优点,但是它也有很多不足之处,这也是为什么需要升级 Sealed Classes 的原因:


sealed class Color {
    class Red : Color()
    class Blue : Color()
    // ...... 更多的颜色
}

sealed class Figure {
    abstract fun draw()
}

class Rectangle(val color: Color) : Figure() {
    override fun draw() {
    }
}

class Round(val color: Color) : Figure() {
    override fun draw() {

    }
}

class Triangle(val color: Color) : Figure() {
    override fun draw() {

    }
}

...... // 随着需求的增加,文件会越发庞大

// 通过快捷键 Mac/Win/Linux:Alt + Enter 自动生成
fun drawFigure(figure: Figure) {
    when (figure) {
        is Rectangle -> TODO()
        is Round -> TODO()
        is Triangle -> when (figure.color) {
            is Color.Blue -> TODO()
            is Color.Red -> TODO()
        }
    }
}

在 Kotlin 1.1 中被限制在一个文件中,随着需求的增加,文件只会越发的庞大,现在放宽了限制之后,我们可以将子类拆分成单独的文件,可以进一步的提高代码的可读性。

正如你所见 Sealed Classes 还是不够灵活,非顶级子类 Yellow 定在 Color 的外面,编译就会出错,而在 Koltin 1.5.0 放宽了限制之后,以上代码可以正常编译通过,不仅提高代码的可读性,而且灵活性也提高了。

除此之外还允许在 Sealed Classes 中声明受保护的构造函数。在 1.5.0 之前所有 Sealed Classes 的默认构造函数都是 private,但是在 1.5.0 之后默认构造函数都是 protected。

sealed class Figure {
    constructor() // 默认为 protected
    private constructor(area: Double) : this()
}

但是不允许声明为 public,因为编译器会自动生成,如果声明为 public 编译就会出错:

一起来看看声明为 protected 的构造函数,反编译后的 Java 代码都做了什么。Tools → Kotlin → Show Kotlin Bytecode

public abstract class Figure {
   private Figure() {
   }

   private Figure(double area) {
      this();
   }

   // $FF: synthetic method
   public Figure(DefaultConstructorMarker $constructor_marker) {
      this();
   }

   // $FF: synthetic method
   public Figure(double area, DefaultConstructorMarker $constructor_marker) {
      this(area);
   }
}

public final class Rectangle extends Figure {
   public Rectangle() {
      super(1.0D, (DefaultConstructorMarker)null);
   }
}

正如你所见,生成的构造方法还是私有的,只不过编译器会自动生成 公有 的构造方法,在子类的构造方法中调用了父类 公有 的构造方法。除此变化之外,另外还有一个重要的特性,增加了密封接口(Sealed Interface)。

注意:

如果你已经升级到 Kotlin 1.5.0,编译器还提示出错,请将下面的代码添加在 build.gradle 中。

compileKotlin {
    kotlinOptions {
        languageVersion = "1.5"
    }
}

Sealed Interface

Sealed Interface 和 Sealed Classes 一样都是用于表示受限制的类层次结构,所以它也拥有 Sealed Classes 所有的优点,Sealed Classes 拥有的特性 Sealed Interface 也都拥有。

但是不同之处在于 Sealed Classes 被限制在单个父类中,而 Sealed Interface 支持更灵活的受限制类层次结构,因为子类可以实现多个 Sealed Interface,如下所示。

// IColor.kt
sealed interface IColor
class Red : IColor
class Blue : IColor

// IArea.kt
sealed interface IArea {
    fun area(): Double
}

// IFigure.kt
sealed interface IFigure

class Round() : IFigure, IColor

class Rectangle(val length: Double, val width: Double) : IFigure, IArea, IColor {
    override fun area(): Double = length * width
}

Sealed Interface 允许子类有多个实现,自由度更高,使得代码更加的灵活,但是它和 Sealed Classes 一样,被限制在了同一个包名和 module 下面,如果违反这个限制编译就会出错。

关于 Sealed Interface 相关的内容,就先介绍到这里,这篇文章主要分析了 Sealed Classes 以及 Sealed Interface 优缺点,在 Kotlin 1.5.0 中还增加了其他的特性,将会在后续的文章中介绍。

全文到这里就结束了,如果有帮助 点个赞 就是对我最大的鼓励
代码不止,文章不停


最后推荐我一直在更新维护的项目和网站:

上一篇 下一篇

猜你喜欢

热点阅读