在迁移到Scala时,应对Java形成的习惯
Java开发人员迁移到Scala时最常犯的错误是什么?
错误我的意思是编写一个不符合Scala精神的代码,例如在类似地图的函数更合适时使用循环,过度使用exception等。
编辑:还有一个是使用自己的getter / setter而不是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") }
后一个示例不会“导出”超出其所需范围的任何变量。 实际上,它根本没有声明任何变量。 这意味着以后更容易重构。 当然,这种方法确实导致了巨大的类文件!
这很简单:Java程序员倾向于编写命令式样式代码 ,而类似Scala的方法则涉及function样式 。
- 这就是Bill Venners在2008年12月的文章“ Scala如何改变我的编程风格 ”中所说明的。
- 这就是为什么有一系列关于“ Scala for Java Refugees ”的文章。
- 这就是关于Scala的一些SO问题的制定方式:“ 帮助重写function风格 ”。
我最喜欢的几个:
-
我花了一段时间才意识到Option是多么有用。 Java中常见的错误是使用null来表示有时没有值的字段/变量。 认识到你可以在Option上使用’map’和’foreach’来编写更安全的代码。
-
学习如何在Scala集合中使用’map’,’foreach’,’dropWhile’,’foldLeft’,…以及其他方便的方法来保存编写你在Java中随处可见的循环结构,我现在认为它是冗长的,笨拙,更难阅读。
一个常见的错误就是疯狂地过度使用Java中没有出现的function。 例如,新手倾向于过度使用模式匹配(*),显式递归,隐式转换,(伪)运算符重载等。 另一个错误是滥用在Java中看起来表面上相似的function(但不是),比如for-comprehension或者甚至if
(它更像Java的三元运算符?:
:)。
(*)选项上有一个很好的模式匹配备忘单: http : //blog.tmorris.net/scalaoption-cheat-sheet/
到目前为止,我还没有采用懒惰的val和溪流。
在开始时,一个常见的错误(编译器发现)是忘记for中的分号:
for (a <- al; b <- bl if (a < b)) // ...
以及收益率的位置:
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-expressions或使用filter。
使用太多的vars而不是vals。
像其他人所说的那样,不使用循环,而是使用list comprehension函数,如map,filter,foldLeft等。如果没有可用的东西(仔细查看应该可以使用的东西),请使用尾递归。
我保留了函数式编程的精神,让我的对象不可变,而不是setter。 所以我做了类似的事情,我返回一个新对象:
class MyClass(val x: Int) { def setX(newx: Int) = new MyClass(newx) }
我尝试尽可能地使用列表。 另外,要生成列表而不是使用循环,请使用for / yield表达式。
使用数组。
这是基本的东西,很容易被发现并修复,但是当它咬你的屁股时,最初会减慢你的速度。
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"