Scala 结构类型铸造不适用于字符串?

Scala 结构类型铸造不适用于字符串?,scala,Scala,鉴于这些定义: type HasMkString = { def mkString(sep:String):String } val name = "John" val names = List("Peter", "Gabriel") 鉴于这些事实: name.mkString("-") // => "J-o-h-n" name.isInstanceOf[HasMkString] // => true names.isInstanceOf[HasMkString] // =>

鉴于这些定义:

type HasMkString = { def mkString(sep:String):String }
val name = "John"
val names = List("Peter", "Gabriel")
鉴于这些事实:

name.mkString("-") // => "J-o-h-n"

name.isInstanceOf[HasMkString] // => true
names.isInstanceOf[HasMkString] // => true
虽然这样做有效:

names.asInstanceOf[HasMkString].mkString("-")
// => Peter-Gabriel
这不起作用:

name.asInstanceOf[HasMkString].mkString("-")

java.lang.NoSuchMethodException: java.lang.String.mkString(java.lang.String)
at java.lang.Class.getMethod(Class.java:1624)
at .reflMethod$Method1(<console>:10)
at .<init>(<console>:10)
at .<clinit>(<console>:10)
at .<init>(<console>:7)
at .<clinit>(<console>)
at $print(<console>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
name.asInstanceOf[HasMkString].mkString(“-”)
java.lang.NoSuchMethodException:java.lang.String.mkString(java.lang.String)
位于java.lang.Class.getMethod(Class.java:1624)
at.reflMethod$Method1(:10)
在。(:10)
在。(:10)
在。(:7)
在
$print()
在sun.reflect.NativeMethodAccessorImpl.invoke0(本机方法)处
在sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)中
在sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)中
位于java.lang.reflect.Method.invoke(Method.java:601)
为什么呢? 是因为String是Java类吗? 我能解决这个问题吗?
这是Scala实现中的错误/缺点吗?

是的,
String
是一个Java类,没有一点帮助就没有
mkString
方法

这是在JVM中运行并使用其本机
String
类以提高速度并与Java代码兼容的一个限制

mkString
通过对
StringOps
的隐式转换,按需添加到
String
。当您将
name
转换为
HasMkString
时,String->StringOps隐式转换将不再适用,因此您将丢失所有额外的方法

解决办法是要么更换

def f (name: HasMkString) = name.mkString ("-")

或者不通过显式转换为结构类型来丢弃类型信息,例如

def f (name: HasMkString) = name.mkString ("-")
f ("John")  // Ok. Scala is smart enough to wrap the String here.
f ("John".asInstanceOf[HasMkString])  // Error. String type is hidden.

或者更好地避免结构类型,因为它可能使用反射并导致较慢的代码。

添加到@artemgr answer中,请注意,
asInstanceOf
不会触发隐式转换:

scala> type HasMkString = { def mkString(sep:String):String }
defined type alias HasMkString

scala> def f (name: HasMkString) = name.mkString ("-")
f: (name: HasMkString)String

scala> f("AAA")
res0: String = A-A-A

谢谢这就解释了。通常我不会做这样的事。我只是试着翻译一段Ruby代码,从最接近的等效代码开始,然后再细化到更惯用的Scala。
scala> type HasMkString = { def mkString(sep:String):String }
defined type alias HasMkString

scala> def f (name: HasMkString) = name.mkString ("-")
f: (name: HasMkString)String

scala> f("AAA")
res0: String = A-A-A