静态类型语言的生命周期方法
在去年,我已经成为一名移动开发人员和功能编程爱好者。
在每一个移动领域,都有组成了生命周期方法的应用程序。 以下将以Android和Kotlin为例,但iOS和Swift也一样。
在Android中,有Activity
的生命周期方法,如onCreate()
。 您也可以定义一个函数onButtonClicked()
,它将完成名称所描述的内容。
为了这个问题的目的,我们假设在onCreate()
中定义了一个变量,用在按钮点击处理函数onButtonClickedPrintMessageLength()
( 通常情况下, onCreate()
实际上是Activity
的设置方法 )。
示例类看起来像这样:
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?
的声明String?
(可空字符串)和使用?.
(空安全呼叫)。 这些都是必须的,因为编译器不能保证在onButtonClickedPrintMessageLength()
之前调用onCreate()
onButtonClickedPrintMessageLength()
。 作为开发者, 我们知道onCreate
总是被称为 * ** 。
我的问题是如何告诉编译器有关这些方法的保证顺序,并消除空检查行为?
*我想有可能new
我们的ExampleActivity
并直接调用onButtonClickedPrintMessageLength()
,从而避开Android框架和生命周期方法,但编译器/ JVM在发生任何有趣的事情之前可能会遇到错误。
**首先调用onCreate
的保证是由Android框架提供的,这是一个外在的真相来源,将来可能会有不同的功能。 看到所有的Android应用程序都基于这个真理来源,但我相信这是值得信赖的。
虽然这不会回答你的实际问题,但在Kotlin中你可以使用lateinit
来告诉编译器,你将在稍后的时间点初始化一个var
:
lateinit var savedStateMessage: String
如果您在初始化之前尝试使用此变量,您将得到一个非常特定的UninitializedPropertyAccessException
。 这个特性在像JUnit这样的用例中非常有用,在这种用例中,你通常在@Before
-annotated方法中初始化变量,在Android Activity
,你不能访问构造函数并在onCreate()
初始化东西。
正如在另一个答案中提到的, lateinit
可以作为一个选项来延迟初始化到保证生命周期中的一个更晚的点。 另一种方法是使用委托人:
var savedStateMessage: String by Delegates.notNull()
这相当于,如果在初始化之前访问该变量,它将报告一个错误。
在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
,否则可能会生成运行时异常。