Die*_*omo 2 android exception class 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)
}
}
}
Run Code Online (Sandbox Code Playgroud)
我试图理解“ 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()
}
Run Code Online (Sandbox Code Playgroud)
但是我得到这个错误:
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)"
Run Code Online (Sandbox Code Playgroud)
为什么会这样呢?
as是Kotlin中投放的关键字。范例:someInstance as CastTarget。Java等效于(CastTarget) someInstance。这些通常是特定于语言的,但是某些语言具有相同的语法。C ++具有与Java相同的语法(尽管它也有一个额外的语法,但这并不重要)。
按钮扩展视图。这意味着一个按钮是一个视图。但是,这并不意味着“视图是”按钮。视图还可以是TextView,ListView,RecyclerView等。视图的列表很长,而且还可以添加更多的库。
这意味着这是有效的:
val view: View = Button(...)
val btn = view as Button
Run Code Online (Sandbox Code Playgroud)
之所以可行,是因为视图在这种情况下是一个Button。但是,如果您有:
val view: View = RecyclerView(...)
val btn = view as Button
Run Code Online (Sandbox Code Playgroud)
它会失败。这是因为,在这种情况下,出于非常明显的原因,RecyclerView不是按钮。View(...) as Button失败的原因是因为视图也不是按钮。强制转换时,只能将实例作为自身或父对象强制转换,而不能将其作为子类强制转换。这是一个实际的例子:
interface Base
class Parent : Base
class Child1 : Parent()
class Child11 : Child1()
class Child2 : Parent()
Run Code Online (Sandbox Code Playgroud)
现在,在这种情况下,这些类是无用的。它们什么也没做,但是仍然可以用来演示继承和强制转换。
现在,说你有这个:
val base = getRandomBaseChild()
Run Code Online (Sandbox Code Playgroud)
这是否意味着您有Child2?这里的推断类型将是Base,这意味着它可以是扩展/实现Base的任何类(或接口,因为Base是接口)。它不一定必须 是Child2,但可以是。由于这种情况下的方法是随机的,因此有时会失败,但并非总是如此:
val child2 = base as Child2
Run Code Online (Sandbox Code Playgroud)
这是因为在某些情况下,基数实际上是Child2。但是对于其他任何实例,它都不是Child2。
假设我们改为使用Child1:
val child1 = base as Child1
Run Code Online (Sandbox Code Playgroud)
实际上,这有两个有效的目标:Child1和Child11。您始终可以向下转换,但是除非类型匹配,否则永远不能向上转换。这样,您现在知道这将永远成功:
val obj = base as Any
Run Code Online (Sandbox Code Playgroud)
因为一切都是Any(/ Object在Java中)。但是,除非类型正确,否则向上转换不一定会成功。
现在,如果您遇到的是实际类型有所不同的情况,那么最简单的方法是使用is:
if(base is Child2) // cast and do something
Run Code Online (Sandbox Code Playgroud)
或者,使用稍微重一些as?。注意,这将添加一个可为空的类型。如果强制转换失败,则为空:
val child2 = base as? Child2 ?: TODO("Cast failed");
Run Code Online (Sandbox Code Playgroud)
您还添加了一些代码;在您的示例中,您将始终能够将Button投射为TextView或View,而TextView可以投射为View。但是,如果将视图强制转换为TextView或Button,则它将失败,因为类型不同。
TL; DR:
视图不是按钮。为了使代码正常工作,请使用val v: View = Button(),然后进行强制转换。v如果声明为父类型的实例实际上是指定的子代,则只能将其转换为子代。您还可以is在转换前用于检查类型是否匹配,或者as?在失败时用于获取null。
您还可以查看Oracle的有关类型和继承的本文。