当Java开发人员迁移到Scala时,最常见的错误是什么?
所谓错误,指的是编写的代码不符合Scala精神的情况,例如在更适合使用类似于map的函数时使用循环、过度使用异常等。
另外一个错误是使用自己编写的getter/setter而不是Scala自动生成的方法。
当Java开发人员迁移到Scala时,最常见的错误是什么?
所谓错误,指的是编写的代码不符合Scala精神的情况,例如在更适合使用类似于map的函数时使用循环、过度使用异常等。
另外一个错误是使用自己编写的getter/setter而不是Scala自动生成的方法。
很简单:Java程序员倾向于编写命令式风格的代码,而更像Scala的方法则涉及函数式风格。
一个显而易见的方法是不利用scala允许的嵌套作用域,以及延迟副作用(或者认识到scala中的所有内容都是表达式):
public InputStream foo(int i) {
final String s = String.valueOf(i);
boolean b = s.length() > 3;
File dir;
if (b) {
dir = new File("C:/tmp");
} else {
dir = new File("/tmp");
}
if (!dir.exists()) dir.mkdirs();
return new FileInputStream(new File(dir, "hello.txt"));
}
可以转换为:
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")
}
后面的例子没有将任何变量“导出”到其所需的范围之外。实际上,它根本没有声明任何变量。这意味着以后更容易重构。当然,这种方法确实会导致类文件极度膨胀!
以下是我最喜欢的两个例子:
花了一段时间我才意识到Option有多么有用。从Java中继承过来的一个常见错误是使用null来表示某个字段/变量有时没有值。请注意,您可以在Option上使用'map'和'foreach'来编写更安全的代码。
学习如何在Scala集合上使用'map'、'foreach'、'dropWhile'、'foldLeft'等其他方便的方法,以节省编写在Java中无处不在的循环结构的时间。我现在认为这些结构冗长,笨拙且难以阅读。
collect
,这是我最喜欢的之一。 - Jus12if
(它更像Java的三元运算符?:
)。Trait
,有时会导致我难以理解自己的代码。 - Jus12使用数组。
这是基础知识,很容易被发现和修复,但当它咬住你的屁股时,最初会减慢你的速度。
Scala有一个Array对象,而在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"
scalaArr[1]
,只有在编译/运行时才能捕获错误(主要使用REPL)。由于我大部分开发工作仍然是Java,所以我没有真正内化这个问题。 - GKelly到目前为止,我还没有采用惰性值和流。
一开始,一个常见的错误(编译器会发现)是在for循环中忘记分号:
for (a <- al;
b <- bl
if (a < b)) // ...
以及在哪里放置yield:
for (a <- al) yield {
val x = foo (a).map (b).filter (c)
if (x.cond ()) 9 else 14
}
而不是
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")
}
使用if语句。通常可以通过使用if表达式或使用filter来重构代码。
过多使用vars而不是vals。
与其他人所说的一样,不要使用循环,而要使用列表推导函数,如map、filter、foldLeft等。如果没有你需要的函数(仔细查看应该会有可用的函数),则使用尾递归。
我保持函数式编程的精神,而不是使用setter,使我的对象不可变。因此,我会像这样返回一个新对象:
class MyClass(val x: Int) {
def setX(newx: Int) = new MyClass(newx)
}