为什么这个映射函数不给出特征的简单名称

Why this map function does not give traits' simple names

我尝试获取所有特征的名称 class 使用 getInterfaces 扩展,其中 returns 特征名称数组。当我手动访问数组的每个成员时,方法 getName returns 像这样的简单名称

trait A
trait B

class C() extends A, B

val c = C()
val arr = c.getClass.getInterfaces
arr(0).getName // : String = A
arr(1).getName // : String = B

但是,当我在 arr 上使用 map 函数时。结果数组包含特征名称的神秘版本

arr.map(t => t.getName) // : Array[String] = Array(repl$.rs$line$A, repl$.rs$line$B)

这个问题的目的不是关于如何获得包含简单名称的结果数组(为此,我可以只使用 arr.map(t => t.getSimpleName)。)我很好奇的是为什么访问数组手动和使用 map 不会产生兼容的结果。我认为这两种方式是等价的吗?

问题不在于 map 而在于 Array,尤其是它的 toString 方法 (这是不使用 [=13 的众多原因之一=]).

实际上,在这种情况下,情况更糟,因为 REPL 做了一些奇怪的事情来尝试漂亮打印 Arrays,在这种情况下效果不佳 (而且,恕我直言, 只会增加混乱)

您可以像这样直接调用 mkString 来解决这个问题:

val arr = c.getClass.getInterfaces
val result = arr.map(t => t.getName)
val text = result.mkString("[", ", ", "]")
println(text)

但是,我宁愿建议根本不使用 Array,而是尽快将其转换为适当的集合 (例如 List可能像:

val interfaces = c.getClass.getInterfaces.toList
interfaces .map(t => t.getName)

Note: About the other reasons for not using Arrays

  • They are mutable.
  • Thet are invariant.
  • They are not part of the collections hierarchy thus you can't use them on generic methods (well, you actually can but that requires more tricks).
  • Their equals is by reference instead of by value.

我相信你 运行 Scala REPL 或 Ammonite 中的东西。

当你定义:

trait A
trait B

class C() extends A, B

classes ABC 未在根包的顶层定义。 REPL 创建一些隔离环境,编译代码并将结果加载到一些内部“匿名”命名空间中。

但事实并非如此。此字节码的创建位置反映在 class 名称中。所以显然有一些与

相似(不一定相同)的东西
// repl$ suggest object
object repl {

  // .rs sound like nested object(?)
  object rs {
   
    // $line sounds like nested class
    class line { /* ... */ }
  
    // $line sounds like the first anonymous instance of line
    new line { trait A }
    // import from `above

    // $line sounds like the second anonymous instance of line
    new line { trait B }
    // import from above
    //...
  }
}

这是因为 REPL 中的范围界定工作方式:新行创建一个新范围,其中包含以前的定义和新添加的定义(可能掩盖了一些旧定义)。这可以通过创建一段新代码作为新匿名 class 的代码、编译它、读入 class 路径、实例化并导入其内容来实现。通过将每个新行放入单独的 class REPL 能够按步骤编译和 运行 事情,而无需等待您告诉它脚本已完成并关闭。

当您使用 运行 时间反射访问 class 名称时,您会看到事物评估方式的人工产物。一条路径可能会通过 REPL 美化器来隐藏这些东西,而另一条路径会绕过它们,因此您可以看到 JVM 看到的原始值。