鉴于一个案例类 - 不幸的是 - 覆盖了toString方法,绕过该方法的任何方法?
即:
val foo = Foo("Hello World")
println(foo)
然后
foo
将产生
Hello World
如果我刚收到Foo
(但不是foo
),我可以对server {
listen 80;
server_name example.com;
if ($args ~ "_escaped_fragment_=(.+)") {
set $foo $1;
rewrite ^ /snapshots$uri$foo?;
}
}
执行任何操作,以便打印
Foo(Hello World)
而不只是字符串?
答案 0 :(得分: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)"
答案 1 :(得分:2)
使用{0}所示的Show
类型类是一个不错的选择。不幸的是toString
是如此普遍(许多代码依赖它来格式化给定对象的字符串),在很多情况下,类型类不会对你有任何好处。例如,如果您有List[Foo]
,则在其上调用toString
会调用Foo.toString
。使用类型类的正确解决方案是为列表定义一个实例,它本身将在show
个实例上调用Foo
。但这只能将问题推得更远,因为很可能会将此列表传递给您无法控制的某些第三方代码,并且会调用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
}
Repl测试:
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
的方法。如果有的话,它表明在toString
中依赖于Any
方法并不是最好的设计,但是我们必须生活和使用大量代码(包括整个Java生态系统)做到了。
正如上面的解决方案一样笨拙,如果您无法控制谁将调用Foo.toString
(意味着您无法将该调用更改为其他内容,例如Show.show
)不能做得更好。
答案 2 :(得分:0)
您基本上是在询问是否可以在运行时修补特定实例的toString
方法。
这在scala这样的语言中是不可能的。
您可以使用implicits使用自定义方法扩展Foo
(请参阅0 __的答案中的类型类示例),但无法更改toString
方法的实现。