【问题标题】:Scala - bypass overridden toString methodScala - 绕过重写的 toString 方法
【发布时间】:2015-07-21 11:25:17
【问题描述】:

给定一个案例类 - 不幸的是 - 覆盖了 toString 方法,有什么方法可以绕过该方法?

即:

case class Foo(s:String){
    override def toString = s
}

然后

val foo = Foo("Hello World")
println(foo)

将产生

你好世界

如果我只是得到foo(但不是Foo),我可以对foo 做任何事情以便它打印

Foo(Hello World)

而不仅仅是字符串?

【问题讨论】:

  • 您可以调用 println(ScalaRunTime._toString(foo)),因为这是默认的案例类 toString 实现 - 这就是您想要的吗?

标签: scala overriding tostring pretty-print


【解决方案1】:

使用 0__ 所示的 Show 类型类是一个不错的选择。不幸的是,toString 非常流行(很多代码依赖它来格式化给定对象的字符串),以至于在很多情况下类型类对你没有任何好处。例如,如果您有一个List[Foo],则在其上调用toString 将调用Foo.toString。使用类型类的正确解决方案是为列表定义一个实例,它本身将在 Foo 实例上调用 show。但这只会将问题推得更远,因为很可能您正在将此列表传递给您无法控制的某些第三方代码,这将调用List.toString(而不是Show.show)。在这种特定情况下,唯一可行的解​​决方案是将Foo 类包装在您自己的类中(例如MyFoo)并在那里覆盖toString。显然,这只有在您可以将 List|[Foo] 更改为 List[MyFoo] 时才有用

implicit class MyFoo(val foo: Foo) extends AnyVal {
    override def toString = s"Foo(${foo.s})"
}
object MyFoo {
 implicit def unwrap(myFoo: MyFoo): Foo = myFoo.foo
}

重复测试:

scala> val list1: List[Foo] = List(Foo("foo"), Foo("bar"))
list1: List[Foo] = List(foo, bar)

scala> val list2: List[MyFoo] = List(Foo("foo"), Foo("bar"))
list2: List[MyFoo] = List(Foo(foo), Foo(bar))

如您所见,list2 的字符串表示使用您自己的toString 实现。 显然,这种解决方案并不理想。在许多情况下它甚至可能完全不切实际(例如,您不能将MyFoo 传递给期望Foo 的方法)。如果有的话,它表明依赖于Any 中的toString 方法并不是最好的设计,但可惜我们必须使用大量代码(包括整个Java 生态系统)来实现这一点。

因此,与上述解决方案一样笨拙,如果您无法控制谁将调用 Foo.toString(这意味着您无法将该调用更改为其他任何内容,例如 Show.show),您可能无法做得更好。

【讨论】:

    【解决方案2】:

    不,您不能更改值“ad-hoc”的方法。您将需要依赖类型类来代替

    trait Show[A] {
      def show(x: A): String  // replaces the `toString` method
    }
    
    // allows you to write foo.show instead of implicitly[Show[Foo]].show(foo)
    implicit class ShowOp[A](private val x: A) extends AnyVal {
      def show(implicit s: Show[A]): String = s.show(x)
    }
    
    case class Foo(s: String) { override def toString = s }
    
    val foo = Foo("bar")
    foo.show   // error -- no implicit for Show[Foo]
    
    // define the Show type class for foo
    implicit object ShowFoo extends Show[Foo] {
      def show(f: Foo) = s"Foo(${f.s})"
    }
    
    foo.show  // ok: "Foo(bar)"
    

    【讨论】:

      【解决方案3】:

      您基本上是在问是否可以在运行时修补特定实例的 toString 方法。

      这在 scala 这样的语言中是不可能的。

      您可以使用隐式使用自定义方法扩展 Foo(请参阅 0__ 答案中的类型类示例),但无法更改 toString 方法的实现。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-07-12
        • 1970-01-01
        • 2017-01-25
        • 1970-01-01
        • 2017-01-23
        • 1970-01-01
        • 2010-12-25
        • 1970-01-01
        相关资源
        最近更新 更多