为什么 Scala 在 class 名称的末尾放置一个美元符号?

Why does Scala place a dollar sign at the end of class names?

在 Scala 中,当您查询一个对象的 class 或 class 名称时,您会在末尾得到一个流氓美元符号 ("$")打印输出:

object DollarExample {
  def main(args : Array[String]) : Unit = {
    printClass()
  }

  def printClass() {
    println(s"The class is ${getClass}")
    println(s"The class name is ${getClass.getName}")
  }
}

结果为:

The class is class com.me.myorg.example.DollarExample$
The class name is com.me.myorg.example.DollarExample$

当然,手动删除末尾的“$”很简单,但我想知道:

这是编译到 JVM 的结果。要在 scala 中创建一个 object 需要两个 classes。 "base" class 和 class 来创建单例对象。因为这些 class 不能同名,所以附加了 $。您可能会修改编译器,使其不会生成 $,但您仍然需要一些方法来命名生成的 class 名称。

您在这里看到的是由于 scalac 将每个 object 编译为两个 JVM classes。末尾带有 $ 的实际上是实现实际逻辑的真正单例 class,可能继承自其他 classes and/or 特征。没有 $ 的是 class 包含 static 转发器方法。我想这主要是为了 Java 互操作。而且还因为您实际上需要一种在 Scala 中创建静态方法的方法,因为如果您想 运行 JVM 上的程序,您需要一个 public static void main(String[] args) 方法作为入口点。

scala> :paste -raw
// Entering paste mode (ctrl-D to finish)

object Main { def main(args: Array[String]): Unit = ??? }

// Exiting paste mode, now interpreting.


scala> :javap -p -filter Main
Compiled from "<pastie>"
public final class Main {
  public static void main(java.lang.String[]);
}

scala> :javap -p -filter Main$
Compiled from "<pastie>"
public final class Main$ {
  public static Main$ MODULE$;
  public static {};
  public void main(java.lang.String[]);
  private Main$();
}

我认为您对此无能为力。

你的方法有几个问题:

  • 您正在使用 Java 反射。 Java 反射对 Scala 一无所知。
  • 此外,您正在对单例对象使用 Java 反射,这个概念在 Java.
  • 中甚至不存在
  • 最后,您正在使用 Java 反射来请求单例对象的 class,但在 Scala 中,单例对象不是 class 的实例。

因此,换句话说:您要求错误语言的反射库来反映它不理解的东西,return 甚至不存在的东西。难怪你会得到毫无意义的结果!

如果改用 Scala 反射,结果会更加合理:

import scala.reflect.runtime.{universe => ru}
def getTypeTag[T: ru.TypeTag](obj: T) = ru.typeTag[T]

object Foo

val theType = getTypeTag(Foo).tpe
//=> theType: reflect.runtime.universe.Type = Foo.type

如您所见,Scala 反射 return 是 Foo 的正确类型,即单例类型(Java 中不存在的另一种类型)Foo.type.

一般来说,Java 反射主要处理 classes,而 Scala 反射处理类型。

使用 Scala Reflection 而不是 Java Reflection 不仅更好,因为 Java Reflection 根本不理解 Scala 而 Scala Reflection 可以(事实上,Scala Reflection 实际上只是一个不同的接口调用编译器,这意味着 Scala 反射知道编译器所做的一切),它还有一个额外的好处,它适用于 Scala 的所有实现,而你的代码会在 Scala.js 和 Scala-native 上中断,这只是不要Java反射。

虽然所有提到 Java 反射机制的答案都是正确的,但这仍然没有解决 $ 符号或 class 名称末尾的“.type”的问题。

你可以用Scala的classOf函数绕过反射的问题。

示例:

println(classOf[Int].getSimpleName)
println(classOf[Seq[Int]].getCanonicalName)
=> int
=> scala.collection.Seq
=> Seq

这样你就得到了和你在 Java

中得到的结果一样的结果