Scala的哪些特性无法翻译成Java?

9
Scala编译器直接编译成Java字节码(或.NET CIL)。Scala的某些功能可以直接在Java中重新执行(例如简单的for循环,类,翻译匿名/内部函数等)。哪些功能无法以这种方式翻译?
这可能主要是学术上的兴趣。更有用的是,您使用的Scala的关键功能或习惯用法在Java中无法轻松表示?
反过来呢?有没有在Java中可以直接完成但在Scala中没有直接等效的事情?在Java中不可转换的习语?

好问题有好答案。通过比较Scala和Java,人们终于意识到所有语言都同样强大,但提供不同的抽象,具有不同的(缺)点。 - Raphael
8个回答

11

特质是一种没有相应的等价物。特质是带有代码的接口。您可以将代码复制到所有混入特质的类中,但那并不是同一件事情。

此外,我认为Scala类型系统更加完整。虽然它最终会映射到JVM类型(实际上受到擦除的影响)。您可以在Scala类型系统中表达一些在Java中不可能的东西(例如变量)。


11

在我看来,这个问题没有理解到重点,因为它让我们通过比较生成的字节码来比较JVM语言。

Scala编译成等效于Java的字节码。也就是说,该字节码可以由用Java编写的代码生成。实际上,您甚至可以让scalac输出一个中间形式,看起来很像Java。

所有功能,例如特质(通过静态前导器)、非本地返回(通过异常)、惰性值(通过引用)等,都可以通过Java程序表达,尽管这可能以最丑陋的方式!

但是使Scala成为Scala而不是Java的原因,在于scalac能够在生成字节码之前为您完成的工作。作为一种静态类型语言,scalac具有检查程序正确性的能力,包括在编译时根据其类型系统进行类型正确性检查。

因此,Java和Scala之间的主要区别(当然,Java也是静态类型的)在于Scala的类型系统,它能够表达Java语言的类型系统不能表达的编程关系。例如:

class Foo[M[_], A](m : M[A])
trait Bar[+A]

在Java的世界里,这些概念,比如M是一个具有自身类型参数的类型参数或者Bar是协变的,就不存在。


我本以为并非所有Java字节码都可以从Java源程序生成,这并非必然如此。因此,Scalac可能会生成javac无法生成的代码,反之亦然。 - The Archetypal Paul
这是正确的,但Scala并没有采取这种路线。Scala发出的几乎所有字节都可以从Java程序中发出。但是,根本的问题在于,不是“字节”定义了Scala以及Scala对您的用途能做什么。 - oxbow_lakes
同意。尽管如此,我很好奇它们之间的区别。(背景:我的Java知识非常有限,我正在学习Scala。到目前为止,我喜欢我所了解的Scala,但我想知道当我回到Java时会遇到多少挫折。这就像我学习Lisp一样-那改变了我编写所有语言的方式,但在像C这样的语言中有些Lisp的东西几乎是不可能实现的。) - The Archetypal Paul
1
从经验来看,如果你被迫使用Java,那么你将会遇到很多挫折。:) 表达能力的差异并不像Lisp和C之间的差异那样明显;毕竟Scala没有宏,而且至少Java有一个类型系统,尽管是相对受限的。我认为,即使你可以将函数表示为Java对象,你也会错过简洁的函数语法。除此之外,弱类型系统和缺乏多重实现继承可能是返回Java时最令人沮丧的问题(它们非常令人沮丧)。 - Aaron Novstrup

4

我认为,在动态混入一些特质方面没有与之相等的。在Scala中,您可以在创建新对象的同时添加某些特质,将其混入。

例如,我们创建了一个又饥饿又口渴的狗和一个只是饥饿的狗。

val hungryThirstyDog = new Dog with Hungry with Thirsty
val onlyHungryDog = new Dog with Hungry

我不知道Java中是否有相应的方法来实现这个。在Java中,继承是静态定义的。


有没有其他技术可以在Java中解决相同的设计问题? - The Archetypal Paul
@Paul 定义 "solve", "easily represented", "express naturally" 等术语。在混合组合方面,Java 中使用对象 (has-a) 组合和依赖注入来处理类似的问题,但这些技术会引入不必要的依赖关系和/或需要更多的样板文件。 - Aaron Novstrup
我不会走定义所有术语的路线。我会假设读者可以理解。感谢您的回复。 - The Archetypal Paul
我实际上并不打算让你定义所有这些术语。 :) 我的观点只是问题有点不明确,因为这些术语如此广泛。我肯定不认为混合组成使代码重用变得“容易表示”在Java中,但听起来你可能认为。在设计问题被优化问题所代替的程度上,如果Scala解决方案更加令人满意,那么Java的“解决方案”是否无法解决设计问题?C的“解决方案”是否合格? - Aaron Novstrup

4

在Java中,隐式转换没有直接的等价物。


但是显式的可能可以做到?因此,这是一种便利(确实是有用的),而不是表现力上的差异。我对探索Scala在多大程度上允许您更自然地表达事物(对于某些“自然”值)感兴趣。 - The Archetypal Paul
1
当我们用自然语言表达自己时,我们经常省略掉说话者和听众都认为理所当然的上下文方面(共同基础)。同样,在Scala编程中,我发现从上下文或默认假设中清晰地省略参数或转换是非常自然的。 - Aaron Novstrup

4
Scala的一个特性是通过Manifests实现类型重构。由于JVM从泛型中剥离了所有类型信息,Scala允许您在变量中保留此信息。据我所知,Java反射不能处理这种情况,因为字节码中没有类型参数。
我需要它们的情况是对List类型进行模式匹配。也就是说,我有一个VertexBuffer对象,它存储在GPU上的数据,并且可以从浮点数或整数列表构建。Manifest代码大致如下:
class VertexBuffer[T](data:List[T])(implicit m:Manifest[T]) {
  m.toString.match {
    case "float" => ...
    case "int" => ...
  }
}

这个链接指向一个博客文章,提供更多信息。

也有很多关于此主题的SO页面,例如这个


当然,在Java中,你可以要求调用者显式传递Class<T> - Aaron Novstrup
或者任何像枚举一样的标识符。Scala的更加简洁,但我想这并没有太大的区别。 - nullspace

3
三个词:高阶类型。

0

0

您的主题不清楚,无论是指JVM还是Java语言。鉴于Scala运行在JVM上,这个问题毫无意义,因为我们都知道Scala运行在JVM上。


我认为已经很清楚了,但是为了更加明确,我指的是Java编程语言。 - The Archetypal Paul
那么根据定义,在Java中可以表达任何Scala构造,即使存在大量的样板和噪声。 - mP.
1
不是的,只有在存在一个可以生成所有有效字节码序列的Java程序时才是真的。我不知道这是否正确,但这不是一个必然的事情。而且我在问题中确实使用了“易懂”的词语。Java和Scala相当,因为两者都是图灵完备的,但表达能力、清晰度等可能会有所不同。这就是我想要的。 - The Archetypal Paul
Java是图灵完备的,任何东西都可以。 - mP.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接