【问题标题】:type erasure and Inheritance in scalascala中的类型擦除和继承
【发布时间】:2016-08-29 04:54:59
【问题描述】:

我有以下类层次结构。

trait Item {val id: String}

case class MItem(override val id: String, val name: String) extends Item

class DItem(override val id: String, override val name: String, val name2: String) extends MItem(id, name)

val d = new DItem("1", "one", "another one")
println(d)

预期输出

DItem(1, one, another one)

实际输出

Mitem(1,one)

为什么会这样。推荐什么以便我得到我的对象的真实类型而不是超类的类型。

【问题讨论】:

    标签: scala inheritance type-erasure


    【解决方案1】:

    这不是类型擦除。你得到这个结果是因为DItem 得到了从Mitem 继承的toString() 实现。你必须重写它才能得到你想要的

    class DItem(override val id: String, override val name: String, val name2: String) extends MItem(id, name) {
        override def toString = s"DItem($id, $name, $name2)"
    }
    

    所以这是一个结果:

    scala> val d = new DItem("1", "one", "another one")
    d: DItem = DItem(1, one, another one)
    
    scala> println(d)
    DItem(1, one, another one)
    

    从案例类继承几乎总是一个坏主意,因为除了toString 后继类will also inherit equals and hashCode

    另一个缺点是此类后继类的模式匹配有限,即不可能在 case 分支中使用此类类,并且可能导致混淆错误。

    示例

    case class A(id: String)
    class B(id: String, name: String) extends A(id)
    
    new B("foo", "bar") match {
      case A(id) => println(id)
      case other => println(other)
    }
    

    你可能认为这段代码没有错误,但你会得到

    <console>:17: error: constructor cannot be instantiated to expected type;
     found   : A
     required: B
         case A(id) => println(id)
              ^
    

    但是,如果您明确推断 B 实例的类型,它将起作用

    scala> new B("foo", "bar").asInstanceOf[A] match {
         |   case A(id) => println(id)
         |   case other => println(other)
         | }
    foo
    

    所以...从案例类继承非常容易出错且令人困惑,除非您知道自己在做什么,否则应避免使用。

    【讨论】:

    • 我认为提到case class inheritance is a bad idea 会增加答案。
    • @YuvalItzchakov 完全同意。我会扩展我的答案
    • 我认为“除非你知道你在做什么”意味着不从案例类继承。
    【解决方案2】:

    据我所知,从案例类继承已被弃用。所以案例类只能(应该)从常规类继承。

    【讨论】:

      【解决方案3】:

      做 println 通常调用 toString 上的对象传递就可以了。

      所以你的代码会发生什么,它将调用对象的 toString 实现,它恰好是具有此实现的 MItem。

      所以你需要像这样覆盖 DItem 上的 toString:

      class DItem(override val id: String, override val name: String, val name2: String) extends MItem(id, name) {
          override def toString = s"DItem($id, $name, $name2)"
      }
      

      如果您只想获取对象的类型,可以使用 getClass。

      println(d.getClass)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-12-30
        • 2012-05-28
        • 1970-01-01
        相关资源
        最近更新 更多