【问题标题】:Avoid asInstanceOf in Scala在 Scala 中避免使用 asInstanceOf
【发布时间】:2015-12-13 23:10:10
【问题描述】:

我现在有这样的东西:

object Shared {
  trait Foo {
    type T <: Foo
    def +(other: T): T
  }

  case class IntFoo(value: Int) extends Foo {
    type T = IntFoo
    def +(other: IntFoo): IntFoo = IntFoo(this.value + other.value)
  }
}

object Current extends App {
  import Shared._

  case class Bar(foo: Foo) {
    def +(other: Bar): Bar = Bar(foo + other.foo.asInstanceOf[foo.T])
  }

  val bar1 = Bar(IntFoo(2))
  val bar2 = Bar(IntFoo(3))
  val expected = Bar(IntFoo(5))
  require(bar1 + bar2 == expected)
  println("All OK")
}

这可行,但需要一个 unsafeasInstanceOf,我想在尽可能少地干扰代码的情况下消除它。

我希望编译器能够捕获无效代码,例如:

  val otherBar = Bar(StringFoo("asdf"))
  val invalidSum = bar1 + otherBar // this throws ClassCastException in runtime

我尝试了以下方法:

  case class Bar[FooT <: Foo](foo: FooT) {
    def +(other: Bar[FooT]): Bar[FooT] = {
      Bar(foo + other.foo)
    }
  }  

但我得到了

Error:(35, 27) type mismatch;
 found   : other.foo.type (with underlying type FooT)
 required: Bar.this.foo.T
      Bar(foo + other.foo)
                ^

建议?

【问题讨论】:

    标签: scala types


    【解决方案1】:

    方法Foo.+ 采用Foo 的内部类型T 的值。所以你必须在你的Bar.+ 中指定other 具有相同的内部类型T。您可以使用依赖类型:

    case class Bar[FooT <: Foo](foo: FooT) {
      def +(other: Bar[foo.T]): Bar[foo.T] = {
        Bar(foo + other.foo)
      }
    }
    

    结果也将是 Bar[foo.T],因为 foo.TFoo.+ 根据定义返回的内容。

    无论如何,这适用于 IntFoo 示例,但会产生 StringFoo 示例的编译错误。

    【讨论】:

    • 这可行,尽管 IntelliJ 将其突出显示为错误。
    【解决方案2】:

    这应该是正确的,假设您对输入的方式感到满意:

    object Shared {
      trait Foo[T <: Foo[T]] {
        def +(other: T): T
      }
    
      case class IntFoo(value: Int) extends Foo[IntFoo] {
        def +(other: IntFoo): IntFoo = IntFoo(this.value + other.value)
      }
    }
    
    object Current extends App {
      import Shared._
    
      case class Bar[T <: Foo[T]](foo: T) {
        def +(other: Bar[T]): Bar[T] = Bar(foo + other.foo)  
      }
    
      val bar1 = Bar(IntFoo(2))
      val bar2 = Bar(IntFoo(3))
      val expected = Bar(IntFoo(5))
      require(bar1 + bar2 == expected)
      println("All OK")
    }
    

    【讨论】:

    • 我只是添加对 F 有界多态性的引用(以帮助读者理解约束 T &lt;: Foo[T] 及其含义):twitter.github.io/scala_school/advanced-types.html#fbounded
    • 我不太乐意更改 Foo 的类型,因为它在无数地方使用而没有任何类型参数(就像foo: Foo)并将它们更改为foo: Foo[_] 会有一个很多变化。
    【解决方案3】:
    case class Bar[+F <: Foo](foo: F) {
      def +(other: Bar { type T = foo.T }): Bar =
        Bar(foo + other.foo)
    }
    

    【讨论】:

    • 获取Type mismatch, expected: Bar.this.foo.T, actual F.
    猜你喜欢
    • 1970-01-01
    • 2020-10-24
    • 2019-08-11
    • 1970-01-01
    • 2020-01-30
    • 2019-02-09
    • 1970-01-01
    • 1970-01-01
    • 2017-09-12
    相关资源
    最近更新 更多