在迁移到Scala时与Java形成的习惯作斗争

在迁移到Scala时与Java形成的习惯作斗争,java,scala,migration,Java,Scala,Migration,Java开发人员在迁移到Scala时最常见的错误是什么 我所说的错误是指编写一个不符合Scala精神的代码,例如在类映射函数更合适时使用循环,过度使用异常等等 编辑:还有一种方法是使用自己的getter/setter,而不是Scala生成的方法。这很简单:Java程序员倾向于编写,而类似Scala的方法则需要一个新的方法 这就是比尔·维纳斯(Bill Venners)在2008年12月发表的文章中所阐述的 这就是为什么有一系列关于“”的文章 这就是有关Scala的一些问题的表述方式:“ 我最喜

Java开发人员在迁移到Scala时最常见的错误是什么

我所说的错误是指编写一个不符合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"