kotlin 运行时检查
上次写了几个常用的Kotlin标准函数(let、run、also、apply),今天再研究几个不流行的标准函数,说不流行,因为以前我们并没有经常这么写,但其功能我们却一直在用。
说到运行时检查,我们经常碰到,比如fragment传递参数,检查下是不是为空,比如运行时权限的检查,等等。看下我们经常在java里写的代码:
public class CheckActivity extends AppCompatActivity {
public static final String ID_KEY = "id_key";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_check);
String id = getIntent().getExtras().getString(ID_KEY);
if (id==null){
finish();
}else{
}
// 根据id去请求数据
}
}
详情页根据列表页传递的id去拿详情数据。在拿数据前要检查有没有id。
运行时检查,是执行下一步操作前,判断状态或者上下文是否有效,很多人在得不到下一步操作的时候,让程序继续运行,而不是抛出异常。
比如第一个例子:如果我们不抛出异常,继续运行,用户会看到一个新页面然后消失,通俗的说就是闪屏一下。这样真的好吗?或者给一个缺省页面,我们需要这样吗?
我更倾向于在开发阶段抛出一个没有id的异常,这样开发人员就能注意到并且去传递这个id,或者去查看为什么id是空值。因为如果你只是 finish() ,你并不知道为什么finish。你不知道是因为id还是别的原因。你也没有帮助用户,提升他的体验。用户不知道发生了什么,你也不知道发生了什么。
这就是为什么我认为这种检查很重要,我们应该在必要时添加它们。除了编写测试以外,这是一个非常好的方法,可以快速找到潜在的错误。
kotlin的一些标准函数,让这项工作更简单了。我们不需要写很多代码去判断然后抛出异常。
require
inline fun require(value: Boolean)
上面的例子我们可以使用 require 去检查id是不是有效的,如果为空他会抛出一个非法参数异常(IllegalArgumentException)。这样我们就可以知道哪里出错了,并去fix代码。
另外它还有一个重载方法,我们可以写一个信息当作第二个参数:
inline fun require(value:Boolean,lazyMessage:() - > Any)
使用这个重载方法,它将更容易调试,并且更快地捕获确切的错误。如果我需要检查的东西是不是null,我们可以requireNotNull直接使用而不需要传递null参数。它也有重载方法:
public inline fun requireNotNull(value: T?, lazyMessage: () -> Any): T {
contract {
returns() implies (value != null)
}
if (value == null) {
val message = lazyMessage()
throw IllegalArgumentException(message.toString())
} else {
return value
}
}
说这么多,都不如show code。我们看kotlin写第一个例子:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_check)
val id = intent.getStringExtra(org.example.syx.check.ID_KEY)
requireNotNull(id , {
"嘿,boy,id是空的。"
})
// 根据id去请求数据
}
而有人会不解,给用户一个崩溃的应用这样的体验真的好吗?非常不好也不应该。我们这样做不是为了在生产环境抛出异常,而是在开发阶段检查你必须的参数,确保开发人员在开发的时候写出健壮的代码,比如例子1中开发者必须检查id,确保传递的参数一定不为空。这样就不会因为参数而crash或者给用户一个空页面或者闪屏。。。
简单的说,我们不应该给用户一个用户不需要的展示。没有id就不该做跳转这样的下一步。
check
inline fun check(value: Boolean) (source)
check看起来和require十分相似,实际上它和require唯一的区别是抛出一个IllegalStateException,而不是一个IllegalArgumentException。这意味着我们应该用它来检查状态,它也有一个重载方法:
public inline fun check(value: Boolean, lazyMessage: () -> Any): Unit {
contract {
returns() implies value
}
if (!value) {
val message = lazyMessage()
throw IllegalStateException(message.toString())
}
}
如果这种状态不是程序需要的,我们需要用这个函数去抛出异常,而不是给用户一个奇怪的页面。