【问题标题】:Scala: Syntax causing runtime error?Scala:导致运行时错误的语法?
【发布时间】:2016-11-28 07:47:30
【问题描述】:

我正在学习 Scala 并尝试编写一些命令行可执行文件。

我有两个版本的 HelloWorld,我认为它们在语义上是相同的。 HelloWorld.scala 从命令行编译并运行成功。 HelloWorld2.scala 编译但产生运行时错误。

我的问题:我认为两者在语义上是相同的,那么为什么第二个会产生运行时错误?

下面是工作示例:

// HelloWorld.scala

object HelloWorld {

  def main(args: Array[String]): Unit = {
    println("Hello, World!")
  }
}

以下是损坏的示例:

// HelloWorld2.scala

object HelloWorld2 {

  def main
    : Array[String] => Unit
    = args          => {
      println("Hello, World!")
    }
}

这是控制台输出:

java.lang.NoSuchMethodException: HelloWorld2.main([Ljava.lang.String;)
    at java.lang.Class.getMethod(Class.java:1778)
    at scala.reflect.internal.util.ScalaClassLoader$class.run(ScalaClassLoader.scala:66)
    at scala.reflect.internal.util.ScalaClassLoader$URLClassLoader.run(ScalaClassLoader.scala:101)
    at scala.tools.nsc.CommonRunner$class.run(ObjectRunner.scala:22)
    at scala.tools.nsc.ObjectRunner$.run(ObjectRunner.scala:39)
    at scala.tools.nsc.CommonRunner$class.runAndCatch(ObjectRunner.scala:29)
    at scala.tools.nsc.ObjectRunner$.runAndCatch(ObjectRunner.scala:39)
    at scala.tools.nsc.MainGenericRunner.runTarget$1(MainGenericRunner.scala:65)
    at scala.tools.nsc.MainGenericRunner.run$1(MainGenericRunner.scala:87)
    at scala.tools.nsc.MainGenericRunner.process(MainGenericRunner.scala:98)
    at scala.tools.nsc.MainGenericRunner$.main(MainGenericRunner.scala:103)
    at scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala)

【问题讨论】:

  • 它正在寻找HelloWorld2.main,但您只有HelloWorld.main。尝试更改类名以匹配文件,或更改文件名以匹配类(即将损坏的示例中的代码复制到 HelloWord.scala 中)
  • 这里有两个不同的东西:main 方法采用 Array[String] 并具有返回类型 Unit,与 main 方法不采用参数并返回一个函数一个Array[String],返回类型为Unit
  • @TheDark,感谢您指出对象名称。 TBH,这只是我的问题中的一个错字:我正在使用的实际文件正确地命名了该对象。
  • @Jesper,在我最疯狂的想象中,我从未想过一个方法和一个函数不是一回事^_^;

标签: scala syntax semantics


【解决方案1】:

不管 scala 可以将方法转换为函数(使用 eta-expansion,有时是自动的),它们都是 different things 这里。主要区别在于 scala 为 JVM 生成不同的字节码。

谈到你的例子,你实际上定义了一个方法def,它返回一个类Function1的对象:

def main: Function1[Array[String], Unit] //you could even put `val` here

当JVM需要一个签名完全不同的方法时:

def main(args: Array[String]): Unit

因为对于 JVM,函数只是类 FunctionN 的一个实例,所以 scala-compiler 不会自动将其转换为方法。手动转换如下所示:

def main(args: Array[String]): Unit = main.apply(args)

def main: Array[String] => Unit = ...// or `val main`

注意apply只是Function1类的一个方法,()只是调用apply的语法糖。

更新:

只是一个附加信息。正如@som-snytt 所指出的,scalas 跑步者对main 的签名更加灵活,所以:

def main(args: Array[String]): Int

适用于scala HelloWorld,但不适用于java HelloWorld - 它需要Unit (void) 作为返回类型。我还记得用javacscalac 编译Java 代码之间的一些区别。所以重点是scala/scalac 正在发展,所以scala 可能(将来)运行更轻松的main 方法,比如功能接口或其他东西。顺便说一句,它还可以编译和运行脚本。

【讨论】:

  • 只是java 需要那个签名。定义def main(args: Array[String]): Int 你会看到一个严厉的警告,但scala 会运行它。 Scala 可以更加灵活。如果它默认打印结果值会很好。另外,谢谢文档链接。
  • 非常感谢您的现场回答和清晰的解释。我一直在尝试用不惯用的方式编写我的 Scala,将所有内容的类型签名写在单独的行上。不幸的是,看来我不得不停止这样做了。
猜你喜欢
  • 1970-01-01
  • 2017-05-28
  • 1970-01-01
  • 2021-12-20
  • 1970-01-01
  • 2014-08-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多