为啥 Scala 在类名的末尾放置一个美元符号?

Posted

技术标签:

【中文标题】为啥 Scala 在类名的末尾放置一个美元符号?【英文标题】:Why does Scala place a dollar sign at the end of class names?为什么 Scala 在类名的末尾放置一个美元符号? 【发布时间】:2022-01-22 12:19:46 【问题描述】:

在 Scala 中,当您查询对象的类或类名时,您会在打印输出的末尾看到一个恶意美元符号(“$”):

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$

当然,最后手动删除“$”很简单,但我想知道:

为什么会在那里?和 还有没有“configure Scala”可以省略?

【问题讨论】:

【参考方案1】:

这是编译到 JVM 的结果。在 Scala 中创建 object 需要两个类。 “基”类和制作单例对象的类。因为这些类不能都具有相同的名称,所以附加了 $。您可能可以修改编译器,使其不会生成$,但您仍然需要一些方法来命名生成的类名。

【讨论】:

【参考方案2】:

您在此处看到的情况是由于 scalac 将每个 object 编译为两个 JVM 类。最后带有 $ 的那个实际上是实现实际逻辑的真正单例类,可能继承自其他类和/或特征。没有 $ 的是一个包含 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$();

我认为您对此无能为力。

【讨论】:

想象如果scalac 将所有objects 编译为具有静态方法的类;一些基本的东西,比如trait Me def test() = println("hello")object MeAgain extends Me override test() = println("hello with me") - 这是不可能的。【参考方案3】:

您的方法存在几个问题:

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

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

如果您改用 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 反射为Foo 返回正确的类型,即单例类型(Java 中不存在的另一种东西)Foo.type

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

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

【讨论】:

【参考方案4】:

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

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

例子:

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

这样你就可以得到与 Java 一样的结果

【讨论】:

以上是关于为啥 Scala 在类名的末尾放置一个美元符号?的主要内容,如果未能解决你的问题,请参考以下文章

是否有类似于 Haskell 的 $(美元符号)的 Scala 运算符?

美元英语是dollar,为啥符号是$

替换字符串中的反向引用语法(为啥是美元符号?)

为啥这些文件夹的共享名称会附加美元符号?

我不能在 React JS ClassName 中使用“$”美元符号 [重复]

类型符号与Scala反射镜像之间的关系