Nutel
Nutel

Reputation: 2304

Struggle against habits formed by Java when migrating to Scala

What are the most common mistakes that Java developers make when migrating to Scala?

By mistakes I mean writing a code that does not conform to Scala spirit, for example using loops when map-like functions are more appropriate, excessive use of exceptions etc.

EDIT: one more is using own getters/setters instead of methods kindly generated by Scala

Upvotes: 13

Views: 1593

Answers (7)

GKelly
GKelly

Reputation: 3919

Using Arrays.

This is basic stuff and easily spotted and fixed, but will slow you down initially when it bites your ass.

Scala has an Array object, while in Java this is a built in artifact. This means that initialising and accessing elements of the array in Scala are actually method calls:

//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"

Upvotes: 1

JWC
JWC

Reputation: 1745

Using if statements. You can usually refactor the code to use if-expressions or by using filter.

Using too many vars instead of vals.

Instead of loops, like others have said, use the list comprehension functions like map, filter, foldLeft, etc. If there isn't one available that you need (look carefully there should be something you can use), use tail recursion.

Instead of setters, I keep the spirit of functional programming and have my objects immutable. So instead I do something like this where I return a new object:

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

I try to work with lists as much as possible. Also, to generate lists, instead of using a loop, use the for/yield expressions.

Upvotes: 1

user unknown
user unknown

Reputation: 36269

I haven't adopted lazy vals and streams so far.

In the beginning, a common error (which the compiler finds) is to forget the semicolon in a for:

 for (a <- al;
      b <- bl
      if (a < b)) // ...

and where to place the yield:

 for (a <- al) yield {
     val x = foo (a).map (b).filter (c)
     if (x.cond ()) 9 else 14 
 }

instead of

 for (a <- al) {
     val x = foo (a).map (b).filter (c)
     if (x.cond ()) yield 9 else yield 14  // why don't ya yield!
 }

and forgetting the equals sign for a method:

 def yoyo (aka : Aka) : Zirp { // ups!
     aka.floskel ("foo")
 }

Upvotes: 1

VonC
VonC

Reputation: 1329452

It's quite simple: Java programmer will tend to write imperative style code, whereas a more Scala-like approach would involve a functional style.

Upvotes: 9

oxbow_lakes
oxbow_lakes

Reputation: 134340

One obvious one is to not take advantage of the nested scoping that scala allows, plus the delaying of side-effects (or realising that everything in scala is an expression):

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"));
}

Could be converted as:

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"))
}

But this can be improved upon a lot. It could be:

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")
}

The latter example does not "export" any variable beyond the scope which it is needed. In fact, it does not declare any variables at all. This means it is easier to refactor later. Of course, this approach does lead to hugely bloated class files!

Upvotes: 8

Landei
Landei

Reputation: 54584

A common mistake is to go wild and overuse a feature not present in Java once you "grokked" it. E.g. newbies tend to overuse pattern matching(*), explicit recursion, implicit conversions, (pseudo-) operator overloading and so on. Another mistake is to misuse features that look superficially similar in Java (but ain't), like for-comprehensions or even if (which works more like Java's ternary operator ?:).

(*) There is a great cheat sheet for pattern matching on Option: http://blog.tmorris.net/scalaoption-cheat-sheet/

Upvotes: 3

Sam Stainsby
Sam Stainsby

Reputation: 1608

A couple of my favourites:

  1. It took me a while to realise how truly useful Option is. A common mistake carried from Java is to use null to represent a field/variable that sometimes does not have a value. Recognise that you can use 'map' and 'foreach' on Option to write safer code.

  2. Learn how to use 'map', 'foreach', 'dropWhile', 'foldLeft', ... and other handy methods on Scala collections to save writing the kind of loop constructions you see everywhere in Java, which I now perceive as verbose, clumsy, and harder to read.

Upvotes: 7

Related Questions