android - Kotlin 类转换异常
问题描述
我是 Android 开发新手,我在教程中看到了这段代码
class MainActivity : AppCompatActivity() {
private val newNumber by lazy(LazyThreadSafetyMode.NONE) {
findViewById<EditText>(R.id.newNumber) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val listener = View.OnClickListener {v ->
val b = v as Button
newNumber.append(v.text)
}
}
}
我试图理解“as”运算符,所以我编写了以下代码:
fun main(args: Array<String>) {
open class View {
fun a() {
println("This is the View method")
}
}
open class TextView: View() {
fun b() {
println("This is the TextView method")
}
}
open class Button: TextView() {
fun c() {
println("This is the Button method")
}
}
var v = View()
var b = v as Button
b.c()
}
但我得到这个错误:
Exception in thread "main" java.lang.ClassCastException: Simplest_versionKt$main$View cannot be cast to Simplest_versionKt$main$Button
at Simplest_versionKt.main(Simplest version.kt:28)"
为什么会这样?
解决方案
as
是在 Kotlin 中进行强制转换的关键字。示例:someInstance as CastTarget
。Java 等价物是(CastTarget) someInstance
. 这些通常是特定于语言的,但有些语言具有相同的语法。C++ 具有与 Java 相同的语法(尽管它也有一个额外的语法,但这不是重点)。
按钮扩展视图。这意味着,一个按钮就是一个视图。但是,这并不意味着 View 是一个按钮。View 也可以是 TextView、ListView、RecyclerView 等。View 的列表很长,也有添加更多的库。
这意味着这是有效的:
val view: View = Button(...)
val btn = view as Button
这是可行的,因为在这种情况下,视图是一个按钮。但是,如果您有:
val view: View = RecyclerView(...)
val btn = view as Button
它会失败。这是因为,在这种情况下,出于非常明显的原因,RecyclerView 不是按钮。失败的原因View(...) as Button
是因为 View 也不是按钮。转换时,您只能将实例转换为自身或父类,但不能转换为子类。这是一个实际的例子:
interface Base
class Parent : Base
class Child1 : Parent()
class Child11 : Child1()
class Child2 : Parent()
现在,在这种情况下,这些类是无用的。它们不做任何事情,但它们仍然可以用来演示继承和强制转换。
现在,假设你有这个:
val base = getRandomBaseChild()
这是否意味着你有一个Child2
?这里推断的类型是Base
,这意味着它可以是扩展/实现 Base 的任何类(或接口,因为 Base 是一个接口)。它不一定是 Child2,但它可以是。由于这种情况下的方法是随机的,因此有时会失败,但并非总是如此:
val child2 = base as Child2
这是因为在某些情况下,基础实际上是 Child2。但对于任何其他实例,它都不是 Child2。
假设我们取了 Child1:
val child1 = base as Child1
这实际上有两个有效目标:Child1 和 Child11。您总是可以向下转换,但除非类型匹配,否则永远不要向上转换。有了它,你现在知道这将永远成功:
val obj = base as Any
因为一切都是Any
(Object
Java 中的 /)。但是除非类型正确,否则向上转换不一定会成功。
现在,如果您遇到这种类型实际上有所不同的情况,最简单的方法是使用is
:
if(base is Child2) // cast and do something
或者,有一种使用as?
. 请注意,这将添加一个可为空的类型;如果强制转换失败,你会得到 null:
val child2 = base as? Child2 ?: TODO("Cast failed");
您还添加了一些代码;在您的示例中,您将始终能够将 Button 转换为 TextView 或 View,并且 TextView 可以转换为 View。但是,如果您将 View 转换为 TextView 或 Button,它将失败,因为类型不同。
TL;博士:
视图不是按钮。为了使您的代码正常工作,请使用val v: View = Button()
,然后进行转换。v
如果声明为父类型的实例实际上是指定的子类型,则只能强制转换为子类型。您还可以is
在强制转换之前检查类型是否匹配,或者as?
如果失败则用于获取 null。
您还可以查看Oracle关于类型和继承的这篇文章。
推荐阅读
- python - 如何从python中的字符串输入推断运算符优先级?
- javascript - 如何使电子应用程序在低于最低支持版本的 MacOS 版本上运行?
- iterator - 在迭代或反向迭代时在 std:set 中插入元素
- c# - c# 将派生类分配给基本泛型变量
- python - 如何运行带有参数的 python 文件作为另一个 python 文件中的变量?
- python - 获取作为参数传递给函数的所有数据框名称的列表
- java - 如何将 Kotlin Nothing 翻译成 Java?
- php - 脚本头过早结束:CGI、Nagios、LDAP
- apache-kafka - 为什么Kafka的吞吐量比Redis高?
- networking - Terraform 可以在定义的 IP 范围上创建计算引擎吗?