代码之家  ›  专栏  ›  技术社区  ›  Community wiki

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

  •  13
  • Community wiki  · 技术社区  · 1 年前

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

    我所说的错误是指编写不符合Scala精神的代码,例如,当类似映射的函数更合适时使用循环,过度使用异常等。

    编辑:还有一个是使用自己的getter/setter,而不是Scala友好生成的方法

    7 回复  |  直到 13 年前
        1
  •  9
  •   VonC    7 年前

    很简单:Java程序员倾向于编写 imperative style code ,而更类似Scala的方法则需要 functional style

        2
  •  8
  •   tsimon    13 年前

    一个明显的问题是不要利用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")
    }
    

    后一个示例不会“导出”超出所需范围的任何变量。事实上,它没有声明任何变量 完全 。这意味着以后更容易重构。当然,这种方法 导致类文件极度膨胀!

        3
  •  7
  •   Sam Stainsby    13 年前

    我最喜欢的几个:

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

    2. 了解如何使用“map”、“foreach”、“dropWhile”、“foldLeft”、。。。以及Scala集合上的其他方便方法,以避免编写Java中随处可见的那种循环构造,我现在认为这种构造冗长、笨拙且难以阅读。

        4
  •  3
  •   Landei    13 年前

    一个常见的错误是,一旦你“摸索”了Java中不存在的功能,就会变得疯狂并过度使用它。例如,新手倾向于过度使用模式匹配(*)、显式递归、隐式转换、(伪)运算符重载等。另一个错误是滥用Java中表面上相似的功能(但不是),比如用于理解甚至 if (更像Java的三元运算符 ?: )。

    (*)Option上有一个很好的模式匹配备忘单: http://blog.tmorris.net/scalaoption-cheat-sheet/

        5
  •  1
  •   user unknown    13 年前

    到目前为止,我还没有采用懒惰的vals和streams。

    一开始,一个常见的错误(编译器发现)是忘记了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")
     }
    
        6
  •  1
  •   JWC    13 年前

    使用if语句。您通常可以重构代码以使用if表达式或使用filter。

    使用过多的vars而不是vals。

    不要像其他人所说的那样使用循环,而是使用列表理解函数,如map、filter、foldLeft等。如果没有可用的函数(仔细看,应该有可以使用的函数),请使用尾部递归。

    我保留了函数式编程的精神,并使我的对象不可变,而不是setter。因此,我做了这样的操作,返回一个新对象:

    class MyClass(val x: Int) {
        def setX(newx: Int) = new MyClass(newx)
    }
    

    我尽量使用列表。此外,要生成列表,请使用for/yield表达式,而不是使用循环。

        7
  •  1
  •   GKelly    13 年前

    使用数组。

    这是基本的东西,很容易被发现和修复,但当它咬到你的屁股时,最初会让你慢下来。

    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"