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

为什么Scala在类名的末尾放一个美元符号?,scala,reflection,classname,Scala,Reflection,Classname,在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

在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$
当然,最后手动删除“
$
”非常简单,但我想知道:

  • 为什么会在那里?;及
  • 是否有必要“配置Scala”来省略它

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

      这里看到的是scalac将每个
      对象编译为两个JVM类。最后带有$的实际上是实现实际逻辑的真正的单例类,可能继承自其他类和/或trait。没有$的类是一个包含
      静态
      转发器方法的类。我想这是为了Java互操作。另外,因为您实际上需要一种在scala中创建静态方法的方法,因为如果您想在JVM上运行程序,您需要一个
      publicstaticvoidmain(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$();
      }
      
      scala>:粘贴-原始
      //进入粘贴模式(按ctrl-D键完成)
      对象Main{def Main(args:Array[String]):Unit=???}
      //正在退出粘贴模式,现在正在解释。
      scala>:javap-p-filter Main
      从“”编译
      公开期末班{
      公共静态void main(java.lang.String[]);
      }
      scala>:javap-p-filter Main$
      从“”编译
      公开期末班主要课程${
      公共静态主模块$;
      公共静态{};
      public void main(java.lang.String[]);
      私人楼宇(元);;
      }
      

      我认为你对此无能为力。

      你的方法有几个问题:

      • 您正在使用Java反射。Java反射对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
      返回正确的类型,即singleton类型(Java中不存在的另一种类型)
      Foo.type

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


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

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

      您可以使用Scala classOf函数绕过反射问题

      例如:

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

      这样,您就得到了与示例Java中相同的结果

      想象一下如果
      scalac
      将所有
      对象
      编译为使用静态方法的类;一些基本的东西,如
      trait Me{def test()=println(“hello”)}
      object MeAgain extensed Me{override test()=println(“hello with Me”)}
      ——这是不可能的。