前语
最近在看一些 Java 源码或 Kotlin 库反编译的 Java class 文件之后,看到了一种之前不曾用过的 Java 语法,标签。本文就运用标签的原因及用法,做一些简略的的总结。
什么是 Java 中的标签
说来惭愧,在去搜 Java 标签这个字眼之前,我甚至都不知道 Java 当中有标签这种语法。那么,什么是标签呢?
kotlin 协程中的标签
作为 Android 开发者,假如你用过 Kotlin 的协程,并尝试去了解过他的内部原理,那么你一定见过标签。
suspend fun getPeople(): String {
withContext(Dispatchers.IO) {
delay(1000)
}
return "mike"
}
suspend fun getAge(name: String): Int {
withContext(Dispatchers.IO) {
delay(1000)
}
return 100 + name.hashCode()
}
比如上面这两个支持挂起的 suspend 办法。咱们用 AS 去检查他反编译的 Java 代码
上面的 label20:
便是标签,当然这两个标签没有任何关系。
Android ViewBinding 中的标签
跟着 kotlin-android-extensions 这个 plugin 被官方废弃,为了不再写 findViewById 的模板代码,又开端运用 ViewBinding 了。那么 ViewBinding 背后的完成又是什么呢?
咱们能够在 application-module 的 app/build/generated/data_binding_base_class_source_out/debug/out/{package_name}/
下看到一切主动生成的 XXXViewBinding 文件;这儿随便找一个文件,看他的 binding 办法。
public static ActivityFlutterRootBinding bind(@NonNull View rootView) {
// The body of this method is generated in a way you would not otherwise write.
// This is done to optimize the compiled bytecode for size and performance.
int id;
missingId: {
id = R.id.flutter_activity;
Button flutterActivity = rootView.findViewById(id);
if (flutterActivity == null) {
break missingId;
}
id = R.id.flutter_container;
FrameLayout flutterContainer = rootView.findViewById(id);
if (flutterContainer == null) {
break missingId;
}
return new ActivityFlutterRootBinding((LinearLayout) rootView, flutterActivity,
flutterContainer, flutterFragment);
}
String missingId = rootView.getResources().getResourceName(id);
throw new NullPointerException("Missing required view with ID: ".concat(missingId));
}
能够看到这儿的 missingId:
便是一个标签。(注意这儿最后界说的 String missingId
和这个标签没有任何关系,这是刚好同名而已。
看起来都是在循环中运用,那么为什么要运用标签呢?他处理了什么问题呢?
为什么运用标签
说起标签,就不得不提 goto ,学过 C 言语的同学应该至少看到过这个关键字,但应该是都没有用过。记住大一上计算机课,教师讲到这个 goto 的时分,非常严肃的说,你们只需知道有 goto 这么个东西就能够了,不要在代码里运用他。
于是乎在后来学习 Java 的时分,当每次看到 goto 的章节时,也是没仔细看就越过了。可能因而就和标签错过了。 。计算机课上教师给出的理由是运用 goto 句子会让代码的跳转逻辑变得混乱。
在 Java 中,在 break 和 continue 这两个关键字的身上,咱们仍能看出一些 goto 的影子。它们并不属于一次跳转,而是间断循环句子的一种办法。
对 Java 来说,仅有用到标签的当地是在循环句子之前。进一步说,它实践需求紧靠在循环句子的前方 —— 在标签和循环之间置入任何句子都是不明智的。而在循环之前设置标签的仅有理由是:咱们期望在其中嵌套另一个循环或许一个开关。这是因为 break 和 continue 关键字一般只间断当前循环,但若调配标签一同运用,它们就会间断并跳转到标签地点的当地开端履行。代码示例:
label1:
outer-iteration {
inner-iteration {
// ...
break; // [1]
// ...
continue; // [2]
// ...
continue label1; // [3]
// ...
break label1; // [4]
}
}
- [1] break 间断内部循环,并在外部循环结束。
- [2] continue 移回内部循环的起始处。但在条件 3 中,continue label1 却一起间断内部循环以及外部循环,并移至 label1 处。
- [3] 随后,它实践是继续循环,但却从外部循环开端。
- [4] break label1 也会间断一切循环,并回到 label1 处,但并不重新进入循环。也便是说,它实践是完全间断了两个循环。
以上内容引用自 《On Java8》。这个简略的比如把标签 的作用描述的酣畅淋漓。
这儿需求纠正一下,其实除了在循环以外,代码块也能够运用标签,上面 Kotlin Coroutine 和 ViewBinding 的比如都是在代码块之前运用标签。
简略了解,从退出循环的视点出发, continue label 和 break label 都能够用 return 句子完成。但是 return 会退出整个办法体,而 continue 或 break 标签并不会,仅仅回退到标签的方位重新开端或整体越过标签继续履行后面的代码。 ,也便是说当咱们仅仅想越过循环的某些变量或许是跳出循环继续履行后续逻辑时,就能够用标签语法了。其实合理运用标签语法会让整个逻辑更好了解。
标签完成了越过办法体中某段固定逻辑的功能。
比如 ViewBinding 的 bind 完成
public static ActivityFlutterRootBinding bind(@NonNull View rootView) {
// The body of this method is generated in a way you would not otherwise write.
// This is done to optimize the compiled bytecode for size and performance.
int id;
missingId: {
id = R.id.flutter_activity;
Button flutterActivity = rootView.findViewById(id);
if (flutterActivity == null) {
break missingId;
}
id = R.id.flutter_container;
FrameLayout flutterContainer = rootView.findViewById(id);
if (flutterContainer == null) {
break missingId;
}
return new ActivityFlutterRootBinding((LinearLayout) rootView, flutterActivity,
flutterContainer, flutterFragment);
}
String missingId = rootView.getResources().getResourceName(id);
throw new NullPointerException("Missing required view with ID: ".concat(missingId));
}
这儿并没有选用遍历当前布局中一切 view 的方式,而是按照界说的内容去履行 findViewById 。因而,这儿运用 label 是一个非常便捷的完成,这儿标签语法有点相似于布尔条件中的与,有任意空值时,直接越过标签地点的代码块,当然,也能够把这个办法改形成带返回值的办法,在调用这个办法的方位依据返回值决定是否抛出反常。
协程中的完成也是相似的原因,因为在一个协程体内内部能够调用另一个协程办法,因而会有多个标签需求一起去处理,而假如简略的运用 return 进行退出,将导致代码结构非常冗杂,简略的运用标签,就能够完成越过整段代码块儿的逻辑。了解了标签的语法规则,再去看协程的完成原理就会明晰很多。
总结
Kotlin 协程和 ViewBinding 中对标签的运用逻辑,是非常好了解的,即便是对不了解标签的人,也是基本上能够一句话解释清楚的逻辑。但是,假如为了运用标签而强行在各个当地用标签,可能会无谓的增添程序的复杂性,阅览起来非常难以了解。在不同类型言语中,相似的语法有时会有不同的作用,不能混为一谈。对于 C 言语这类面向进程的言语, goto 句子运用不当的确会让整个代码的跳转逻辑变得混乱。但是在 Java 这类面向对象的编程言语中,相似的语法却很值得运用。