【问题标题】:Lifecycle methods in statically typed languages静态类型语言中的生命周期方法
【发布时间】:2016-09-04 21:41:51
【问题描述】:

在过去的一年里,我成为了一名移动开发人员和函数式编程的崇拜者。

在每个移动领域中,都有具有生命周期方法的组件构成了应用程序的核心。以下将以 Android 和 Kotlin 为例,但同样适用于 iOS 和 Swift。

在 Android 中,有 Activity 的生命周期方法,例如 onCreate()。您还可以定义一个函数onButtonClicked(),它的作用与名称所描述的完全一样。

出于问题的目的,假设在onCreate() 中定义了一个变量,用于按钮单击处理程序onButtonClickedPrintMessageLength()通常是这种情况 - onCreate() 本质上是Activity' s 设置方法)。

示例类如下所示:

class ExampleActivity: Activity() {
    var savedStateMessage: String? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        savedStateMessage = "Hello World!"
    }

    fun onButtonClickedPrintMessageLength() {
        System.out.println(savedStateMessage?.length)
    }
}

注意savedStateMessage 声明为String?(可空字符串)和?.(空安全调用)的使用。这些是必需的,因为编译器不能保证onCreate() 将在onButtonClickedPrintMessageLength() 之前被调用。不过作为开发人员,we know that onCreate will always be called first* **

我的问题是如何告诉编译器这些方法的保证顺序并消除空值检查行为?

* 我认为可以将new 升级到我们的ExampleActivity 并直接调用onButtonClickedPrintMessageLength(),从而避开Android 框架和生命周期方法,但编译器/JVM 可能会在任何有趣的事情发生之前遇到错误。

** 首先调用onCreate 的保证是由Android 框架提供的,它是一个外部事实来源,将来可能会以不同的方式中断/运行。不过,鉴于所有 Android 应用程序都基于此事实来源,我相信它是安全的。

【问题讨论】:

  • 你不能告诉编译器方法的顺序,但你可以(在你的情况下)强制类型为String并提供一个无用的默认值:var savedStateMessage: String = "default stupid value"
  • 您可能应该将此问题拆分为针对每种相关编程语言/环境的单独问题。如果你不这样做,那么你会遇到一个问题,即使只有一个可以被接受,但几个答案可能是正确的。
  • 我同意你的看法,ColGraff。我应该把这个问题分成两个:一个是针对 Android 的,一个是针对 iOS 的。下次我只问一个,或者两个问题都问。

标签: android ios swift functional-programming kotlin


【解决方案1】:

虽然这不会回答您的实际问题,但在 Kotlin 中,您可以使用 lateinit 告诉编译器您将在稍后的时间点初始化 var

lateinit var savedStateMessage: String

如果您在初始化之前尝试使用此变量,您将获得一个非常具体的UninitializedPropertyAccessException。此功能在 JUnit 等用例中很有用,您通常会在 @Before-annotated 方法中初始化变量,而在 Android Activitys 中,您无法访问构造函数并初始化 onCreate() 中的内容。

【讨论】:

    【解决方案2】:

    正如另一个答案中提到的,lateinit 可用作将初始化推迟到保证生命周期的稍后时间点的选项。另一种方法是使用委托:

    var savedStateMessage: String by Delegates.notNull()
    

    这是等价的,如果你在初始化变量之前访问它会报错。

    【讨论】:

      【解决方案3】:

      在 Swift 中,您可以在这里使用隐式解包的 Optional

      class Example: CustomStringConvertible {
        var savedStateMessage: String! // implicitly-unwrapped Optional<String>
        var description: String { return savedStateMessage }
        init() {
          savedStateMessage = "Hello World!"
        }
      }
      
      print(Example()) // => "Hello World!\n"
      

      通过在示例的第二行中String 的末尾使用运算符!,您可以保证在使用变量之前对其进行设置。这是在示例的init 方法中完成的。它仍然是Optional,但代码可以将其视为String,因为它会在每次使用前自动解包。您必须注意,当变量可能被访问或可能生成运行时异常时,切勿将其设置为 nil

      【讨论】:

      • 如果我不小心在某处写了myCustomStringConvertable.description = null,编译器会产生任何警告吗?理想情况下,当 I 将变量设置为 null 时编译器会对我大喊大叫,但在编译器无法验证它是否已初始化时不会对我大喊大叫。
      猜你喜欢
      • 1970-01-01
      • 2018-02-12
      • 1970-01-01
      • 2015-05-06
      • 1970-01-01
      • 2020-05-02
      • 2021-01-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多