【问题标题】:What are the differences between asInstanceOf[T] and (o: T) in Scala?Scala 中的 asInstanceOf[T] 和 (o: T) 有什么区别?
【发布时间】:2011-03-25 15:12:35
【问题描述】:

我看到Scala中有两种方法可以转换对象:

foo.asInstanceOf[Bar]
(foo: Bar)

当我尝试时,我发现asInstanceOf 不使用隐式转换,而另一个使用。

这两种方法的行为有何不同?建议在哪里使用一个而不是另一个?

【问题讨论】:

    标签: scala casting


    【解决方案1】:

    Scala 编程Chapter 15 - Case Classes and Pattern Matching 中详细介绍了这一点。

    基本上第二种形式可以在模式匹配中用作Typed Pattern,提供isInstanceOfasInstanceOf 功能。比较

    if (x.isInstanceOf[String]) {
      val s = x.asInstanceOf[String]
      s.length
    } else ...
    

    对比

    def checkFoo(x: Any) = x match {
      case s: String => s.length
      case m: Int => m
      case _ => 0
    }
    

    作者暗示isInstance* 处理方式的冗长是有意将您推向模式匹配风格。

    我不确定哪种模式对于没有测试的简单类型转换更有效。

    【讨论】:

      【解决方案2】:
      • foo.asInstanceOf[Bar]cast 类型,主要是运行时操作。它说应该强制编译器相信fooBar。如果foo 在运行时被评估为不是Bar,这可能会导致错误(ClassCastException)。

      • foo:Bar 是一个类型ascription,完全是编译时操作。这是在帮助编译器理解代码的含义,而不是强迫它相信任何可能不真实的东西;使用类型属性不会导致运行时失败。

      类型归属也可用于触发隐式转换。例如,您可以定义以下隐式转换:

      implicit def foo(s:String):Int = s.length
      

      然后确保它像这样使用:

      scala> "hi":Int                                 
      res29: Int = 2
      

      将类型Int 赋予String 通常是编译时类型错误,但在放弃之前,编译器会搜索可用的隐式转换以使问题消失。将在给定上下文中使用的特定隐式转换在编译时是已知的。

      不用说,运行时错误是不可取的,因此您可以在多大程度上以类型安全的方式指定事物(不使用asInstanceof),那就更好了!如果您发现自己使用的是asInstanceOf,那么您可能应该使用match

      【讨论】:

      • 需要类型注释的一种情况是当你想为 null 指定类型时,例如使用 Java API 时。
      • 感谢您的精彩解释!
      • @pelotom:嘿,你的例子很棒! "hi":Int ...我从未见过或使用过那种方式隐含但确实很有趣。
      • foo: Bar 被称为类型归属。使用这个术语会很好,因为它可以更容易地找到有关它的更多信息。或在搜索时找到此答案。 :-)
      • +1 给 Daniel 以获取术语类型归属。否则不会知道
      【解决方案3】:

      Pelotom 的回答很好地涵盖了理论,这里有一些例子可以更清楚地说明:

      def foo(x: Any) {
        println("any")
      }
      
      def foo(x: String) {
        println("string")
      }
      
      
      def main(args: Array[String]) {
        val a: Any = new Object
        val s = "string"
      
        foo(a)                       // any
        foo(s)                       // string
        foo(s: Any)                  // any
        foo(a.asInstanceOf[String])  // compiles, but ClassCastException during runtime
        foo(a: String)               // does not compile, type mismatch
      }
      

      如您所见,类型归属可用于解决歧义。有时它们可​​能无法被编译器解析(见下文),编译器会报告错误,您必须解决它。在其他情况下(如示例中),它只是使用“错误”的方法,而不是您想要的。 foo(a: String) 不编译,表明类型归属不是强制转换。将其与上一行进行比较,编译器很高兴,但您得到了一个异常,因此使用类型属性检测到错误。

      如果你还添加一个方法,你会得到一个无法解决的歧义

      def foo(xs: Any*) {
        println("vararg")
      }
      

      在这种情况下,第一次和第三次调用 foo 将无法编译,因为编译器无法决定是使用单个 Any 参数还是使用可变参数调用 foo,因为它们似乎都一样好=> 你必须使用类型归属来帮助编译器。

      编辑另见What is the purpose of type ascription in Scala?

      【讨论】:

        【解决方案4】:

        有区别的例子:

        1. 类型转换 (asInstanceOf) 是一种运行时操作,可能存在运行时异常。
        2. Ascription 基本上只是在编译时执行的向上转换。

        例子:

        class Parent() { def method() {} }
        class Child1 extends Parent() { def method1() {} }
        class Child2 extends Parent() { def method2() {} }
        
        // we return Parent type
        def getChild1() : Parent = new Child1()
        def getChild2() : Parent = new Child2()
        def getChild()  : Child1 = new Child1()
        
        (getChild1().asInstanceOf[Child1]).method1() // OK
        (getChild1().asInstanceOf[Child2]).method2() // runtime ClassCastException
        
        (getChild1() : Child2).method2() // compile-time error
        (getChild2() : Child2).method2() // compile-time error
        (getChild() : Parent).method1() // compile-time error
        (getChild()).method()  // OK
        
        // with asInstanceOf, we can cast to anything without compile-time error
        getChild1().asInstanceOf[String] // runtime ClassCastException
        getChild1().asInstanceOf[Int] // runtime ClassCastException
        

        我们也可以使用multiple-dispatch来调用方法:

        def prt(p: Parent) = println("parent")
        def prt(ch: Child1) = println("child")
        
        prt(new Parent()) // prints "parent"
        prt((new Child1()) : Parent) // prints "parent"
        prt(new Child1()) // prints "child"
        
        prt(new Parent().asInstanceOf[Child1]) // runtime ClassCastException
        prt(new Child1().asInstanceOf[Parent]) // prints "parent"
        

        我们可以定义隐式转换:

        // after definition of implicit conversions
        implicit def toChild1(p: Parent) : Child1 = new Child1()
        implicit def toChild2(p: Parent) : Child2 = new Child2()
        
        (getChild1() : Child2).method2() // OK - implicit conversion to Child2 in ascription
        (getChild2() : Child2).method2() // OK - implicit conversion to Child2 in ascription
        (getChild2()).method1() // OK - implicit conversion to Child1 when calling method1()
        (getChild2()).method2() // OK - implicit conversion to Child2 when calling method2()
        (getChild2() : Parent).method() // OK - no implicit conversion
        (getChild() : Parent).method1() // OK - implicit conversion to Child1 when calling method()
        
        getChild1().asInstanceOf[Int] // still runtime ClassCastException (no implicit conversion)
        

        【讨论】:

          猜你喜欢
          • 2012-03-10
          • 1970-01-01
          • 1970-01-01
          • 2021-04-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-12-31
          相关资源
          最近更新 更多