在迁移到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风格 ”。

我最喜欢的几个:

  1. 我花了一段时间才意识到Option是多么有用。 Java中常见的错误是使用null来表示有时没有值的字段/变量。 认识到你可以在Option上使用’map’和’foreach’来编写更安全的代码。

  2. 学习如何在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"