在迁移到Scala时与Java形成的习惯作斗争
Java开发人员在迁移到Scala时最常见的错误是什么 我所说的错误是指编写一个不符合Scala精神的代码,例如在类映射函数更合适时使用循环,过度使用异常等等在迁移到Scala时与Java形成的习惯作斗争,java,scala,migration,Java,Scala,Migration,Java开发人员在迁移到Scala时最常见的错误是什么 我所说的错误是指编写一个不符合Scala精神的代码,例如在类映射函数更合适时使用循环,过度使用异常等等 编辑:还有一种方法是使用自己的getter/setter,而不是Scala生成的方法。这很简单:Java程序员倾向于编写,而类似Scala的方法则需要一个新的方法 这就是比尔·维纳斯(Bill Venners)在2008年12月发表的文章中所阐述的 这就是为什么有一系列关于“”的文章 这就是有关Scala的一些问题的表述方式:“ 我最喜
编辑:还有一种方法是使用自己的getter/setter,而不是Scala生成的方法。这很简单:Java程序员倾向于编写,而类似Scala的方法则需要一个新的方法
- 这就是比尔·维纳斯(Bill Venners)在2008年12月发表的文章中所阐述的
- 这就是为什么有一系列关于“”的文章
- 这就是有关Scala的一些问题的表述方式:“
- 我花了一段时间才意识到这个选择是多么的有用。Java带来的一个常见错误是使用null表示有时没有值的字段/变量。认识到您可以在选项上使用“map”和“foreach”来编写更安全的代码
- 了解如何使用“地图”、“foreach”、“dropWhile”、“foldLeft”。。。以及Scala集合上的其他便捷方法,以避免编写Java中随处可见的循环构造,我现在认为这种构造冗长、笨拙且难以阅读
- 我最喜欢的几个:
一个常见的错误是,一旦“摸索”了Java中不存在的特性,就会疯狂地过度使用它。例如,新手往往过度使用模式匹配(*)、显式递归、隐式转换、(伪)运算符重载等。另一个错误是误用在Java中表面上相似的特性(但不是),比如用于理解,甚至是
if
(更像Java的三元运算符?:
)
(*)在选项上有一个很好的模式匹配备忘单:一个明显的备忘是不要利用scala允许的嵌套范围,再加上延迟副作用(或者认识到scala中的一切都是表达式): 可转换为:
def foo(i : Int) : InputStream = {
val s = i.toString
val b = s.length > 3
val dir =
if (b) {
new File("C:/tmp")
} else {
new File("/tmp")
}
if (!dir.exists) dir.mkdirs()
new FileInputStream(new File(dir, "hello.txt"))
}
但这可以改进很多。它可以是:
def foo(i : Int) = {
def dir = {
def ensuring(d : File) = { if (!d.exists) require(d.mkdirs); d }
def b = {
def s = i.toString
s.length > 3
}
ensuring(new File(if (b) "C:/tmp" else "/tmp"));
}
new FileInputStream(dir, "hello.txt")
}
后一个示例不会“导出”超出所需范围的任何变量。事实上,它根本没有声明任何变量。这意味着以后更容易重构。当然,这种方法确实会导致类文件大量膨胀 到目前为止,我还没有采用懒惰的VAL和streams 在开始时,一个常见错误(编译器发现)是忘记for中的分号:
for (a <- al;
b <- bl
if (a < b)) // ...
使用if语句。通常可以重构代码以使用if表达式或使用filter 使用太多变量而不是VAL 不要像其他人说的那样使用循环,而是使用列表理解函数,如map、filter、foldLeft等。如果没有您需要的可用函数(仔细看,应该有您可以使用的函数),请使用尾部递归 我不使用setter,而是保持函数式编程的精神,让我的对象保持不变。因此,我执行类似于这样的操作,返回一个新对象:
class MyClass(val x: Int) {
def setX(newx: Int) = new MyClass(newx)
}
我尽可能多地使用列表。此外,要生成列表,请使用for/yield表达式,而不是使用循环 使用数组
这是基本的东西,很容易被发现和修复,但当它咬到你的屁股时,会让你开始变慢
Scala有一个数组对象,而在Java中这是一个内置工件。这意味着初始化和访问Scala中的数组元素实际上是方法调用:
//Java
//Initialise
String [] javaArr = {"a", "b"};
//Access
String blah1 = javaArr[1]; //blah1 contains "b"
//Scala
//Initialise
val scalaArr = Array("c", "d") //Note this is a method call against the Array Singleton
//Access
val blah2 = scalaArr(1) //blah2 contains "d"
他们使用4个空格的缩进。现在不可能再将queston标记为“社区wiki”。它必须引起主持人的注意。@Daniel-羞耻。。。我喜欢这个功能,我是一个版主,我怎么会错过这个变化呢?@Daniel:在“社区状态”方面,我完全同意Daniel的观点-这显然是一个来自Java和Scala的人真正感兴趣的问题。别忘了我最喜欢的一个东西--收藏,我想知道我们是否可以在不破坏Java集成的情况下得到null弃用。@Malvolio“border methods”(即使可以从Java调用Scala方法,但许多方法都不适合从Java调用,因此我认为Java互操作必须是故意公开的层)我经常过度使用
Trait
,有时我在理解自己的代码时会遇到困难。如果这个答案能说明是什么构成了这些功能的过度使用,那么它会更有帮助。Scala“for”的不同性质无论如何强调都不过分。您只保存了一行代码,失去了大量可读性。@ryeguy:这是一个人为的例子,所以最小化loc并不是我的目标。您关于可读性的说法可能是正确的,但当您开始将这样的方法“放大”到>20行时,认为JavaVerson更具可读性的论点开始变得非常薄弱。你最终会陷入一个笨拙的混乱,方法的各个组成部分都相互混淆;很难重构,因为您无法看到在何处使用了什么。此外,如果您遵循标准模式(所有内部定义在顶部,主体在底部),可读性不一定会受到影响。方法主体可以是一行。从Java迁移到Scala时,与其说是一个坏习惯,不如说是一个“减速”。有很长一段时间,我一直在写scalaArr[1]
,只在编译/运行时捕捉到错误(主要使用REPL)。我并没有真正将其内部化,因为我执行的大多数开发仍然是java。
for (a <- al) {
val x = foo (a).map (b).filter (c)
if (x.cond ()) yield 9 else yield 14 // why don't ya yield!
}
def yoyo (aka : Aka) : Zirp { // ups!
aka.floskel ("foo")
}
class MyClass(val x: Int) {
def setX(newx: Int) = new MyClass(newx)
}
//Java
//Initialise
String [] javaArr = {"a", "b"};
//Access
String blah1 = javaArr[1]; //blah1 contains "b"
//Scala
//Initialise
val scalaArr = Array("c", "d") //Note this is a method call against the Array Singleton
//Access
val blah2 = scalaArr(1) //blah2 contains "d"