X

Sundararajan's Weblog

  • Java
    March 5, 2007

Scala for Java programmers - Part 2

Guest Author

This is continuation of my last blog entry titled Scala for Java programmers.














































































Feature

Java

Scala

Application

Any Java class with public static void main(String[])
method can be run as an application.

public class Hello {public static void main(String[] args) {
System.out.println("hello world");
}
}


Any object with def main(args: Array[String]) : unit method
can be run as an application. [Note: Scala does not have static members - you
have to use singleton objects.]

object Hello {def main(args: Array[String]) : unit = {
Console.println("hello world");
}
}

Also, you could extend Application class and have just code inside object like
in

object Hello extends Application {
Console.println("hello world");
}

But, if you want command line parameters, you have to use the first option.

Sequence-comprehension
is an easier syntax to build a new sequence from existing sequence(s)
by filtering, mapping or combination of those.

Use Java's for-each
loop and build a new list.

General form:
for(<generators>; <filter>) yield <expression>
Example:
object Main extends Application {
val list = List("Fortran", "Java", "Scala",
"JavaScript", "ML");
val shortList = for (val e <- list; e.length() < 6)
yield e.toUpperCase()

Console.println(shortList);
}

You could accomplish everything above by "map", "filter", "flatMap" calls
on List(s). For-comprehension provides an easier syntax to work with. In the above
case, we could have written

object Main extends Application {
val list = List("Fortran", "Java", "Scala",
"JavaScript", "ML");
val shortList = list.filter(e=>e.length() < 6)
.map(e=>e.toUpperCase());
Console.println(shortList);
}

See also: Sequence Comprehensions.

Method references

No. Use java.lang.reflect.Method (but, you'll miss static type checking!)

Method references can be passed as argument whenever a function type value
is expected.

object Main {
def func(s: String) : boolean = {
return s.startsWith("J");
}
def print(s: String): unit = {
Console.println(s);
}
def main(args: Array[String]) : unit = {
var list = List("hello", "world", "Java");
// pass method reference as argument
list = list.filter(func);
Console.println(list);
// explicitly mentions "Main" object
// in the method reference
list.foreach(Main.print);
}
}

Also, remember operators are methods too and numbers are objects too!
So, the following works:

object Main {
def main(args: Array[String]) : unit = {
val list = List(3, 54, -1, -324, 45);
// print negative numbers only
Console.println(list.filter(0 >));
}
}



Pattern Matching
is a techique to classify objects by their run-time type, accessing their
mebers or some other characteristic of group of objects.

No. Following "Matching Objects with Patterns",
in Java you could do:
  • Object-oriented decomposition
  • Visitor pattern
  • Type-Test/Type-Cast (instanceof and cast)


Scala has "match" expression. Think of it as a generalization of Java's
switch statement. While Java's switch statement works only for integers and
enums,
Scala's pattern matching is more general. Simple switch statement equivalent in
Scala is as follows:

object t {
def main(args: Array[String]) : unit = {
args.length match {case 1 => Console.println("1 argument");case 2 => Console.println("2 arguments");case _ => Console.println("more than 2!");
}
}
}

More complex match example:
  def matchTest(x: Any): Any = x match {
// matching integer value "1"
case 1 => "one"
// matches string "two"
// x is a string with "two" as value
case "two" => 2
// x is any int type but with not equal to 1
case y:Int => "scala.Int"
}

Even more complex pattern matchings involve case classes.
Case classes are classes with "case" modifier. With "case" classes, you can
  • create objects of case classes without "new" operator - use function call-like syntax
  • case classes have automatic accessor generators for constructor parameter.
  • case classes can be used in pattern matching.

Example:

abstract class Expression;
case class Number(n:int) extends Expression;
case class Add(left:Expression, right:Expression) extends Expression;
case class Var(name: String) extends Expression;
case class Multiply(left:Expression, right:Expression) extends Expression;
// sample match expression that uses above case classes
// This expression simplifies a given expression "e"
// We use x + 0 = x and x \* 1 = x rules to simplify
def simplify(e: Expression) : Expression = {
e match {
// matches if expression is a Multiply object
// and right operand is Number 1case Multiply(x, Number(1)) => x
// matches if expression is a Add object
// and right operand is Number 0case Add(x, Number(0)) => x
// matches any other expression - no simplification
// just return the original expressioncase _ => e
}
}

References:
See also: Scala case classes
and Scala pattern matching.

Throwing exceptions

throw new RuntimeException();
throw new IOException();


throw new RuntimeException();
throw new IOException();

This is same as Java except that in Scala, there are no checked exceptions.
User is not forced to have "throws" clauses as in Java.

Catching Exceptions

try {
// ...
} catch(IOException ieExp) {
// ...
} catch(RuntimeException re) {
// ...
} finally {
// ...
}

where finally is optional.

try {
// ...
} catch {case ioExp:IOException => {
// ...
}case re:RuntimeException => {
// ...
}
} finally {
// ...
}

Notes:
  • Pattern matching is used to match a
    particular exception type within a single catch clause - unlike
    separate catch clauses in Java.
  • finally clause is optional here as well.
  • Because catch uses case clauses, it is possible
    to match values - for example, if your exception class is
    a case class, then you can match constructor parameters of
    your exception class as well.

case class MyException(s:String) extends Exception(s) {
}
object Hello extends Application {
try {
throw new MyException("hello");
} catch {
// MyException with message equal to "hello"
case MyException("hello") =>
Console.println("world!");
// MyException with message not equal to "hello"
case MyException(_) =>
Console.println("no way!");
// any other exception
case _ =>
Console.println("any other exception");
}
}


Packages

package com.acme;

With the above statement as first line in a compilation unit,
everything in the compilation unit goes into the package

package com.acme;

With the above statement as first line in a compilation unit,
everything in the compilation unit goes into the package --
as in Java. But, Scala supports "packaging" statements as well.
The following example defines


package com.acme {
class X {}
class Y {}
}
package com.sun {
class X {}
package P {
class Y {}
}
}

Notes:

  • Several packages may be declared in the same compilation unit.
  • Packages can include other packages.

The above example defines com.acme, com.sun, com.sun.P packages.


See also: Scala packages.

Import Statements

// import whole package
import java.io.\*;
// import a specific (public) class from a package
import java.net.URL;


// import whole package
import java.io._;
// import a specific (public) class from a package
import java.net.URL;


Static Import

// import all static members of Math class
import static java.lang.Math.\*


No statics in Scala -- we use singleton objects. But, we can
import all members of singleton objects.

object t {
def sayHello() : unit = {
Console.println("hello");
}
}
object Main {import t._;
def main(args: Array[String]): unit = {
sayHello();
}
}

Also, every Java class is exposed a Scala class without
static members and an object that contains only the static
members of the Java class. So, it is possible to import all
static members of a Java class using the object import as
follows:

import java.lang.Math._;
object t {
def main(a: Array[String]) : unit = {
Console.println(sin(3.14/2));
}
}


typedefs
(a.k.a type aliasing).

No (but may be in future?)

Generic pattern is
type <name> = <type-expression>
Examples:
type StringList = List[String];
var sl: StringList = List("hello", "world");
type voidFunc = ()=>unit;
var v : voidFunc = ()=>Console.println("hello");
v();


Abstract Types
(using abstract type members in classes
in addition to abstract value members)

No. Use generics!

In Scala, classes can have type members in addition to value members. Also,
like value members (say a method being abstract), type members could be abstract
as well. Example:

abstract class Cell {
// an abstract type member
// subclass can define ittype T
// abstract value member
var element: T
}
abstract class ValueCell extends Cell {
// define T to be some subtype of AnyVal
type T <: AnyVal
}
class IntCell extends Cell {
// define T to be int
type T = int
// define (initial) value to be zero
var element : T = 0
}

See also: Scala Abstract Types.

Currying

Not supported

A method or function can have multiple parameter-sections or segments.
Each parameter section can be curryed. Example:

object Main {
def main(args: Array[String]) : unit = {
def sum(x: int)(y: int) : int = x + y
// curry sum to specify first value
val add7 = &sum(7)
// call curried version with one argument
Console.println(add7(8));
}
}

Notes:
  • define with multiple parameter sections so that it can be curried.
  • Normal call would look like sum(7)(8)
  • Each parameter section can have it's
    own variadic parameter - i.e., you could have each parameter section ending
    with T\* type.

The following example accepts multipled ints
and multiple strings as parameters.

def printAges(values: int\*)(names: String\*) : unit = {
// code here..
}
// caller calls this as
printAges(34, 24, 45)("X", "Y", "Z");


Call-by-name

Not supported.

object Main {
def func(v: => int) = {
Console.println("You passed " + v);
Console.println("You passed " + v);
}
def main(args: Array[String]): unit = {
var i = 0;
func({i = i + 1; i});
}
}

Without "=>" before "int", you would get the output
You passed 1
You passed 1

But, with the "=>", you would get the output
You passed 1
You passed 2

Interesting blog entry to simulate Java-style for(<init< <condition> <update>)
loop for Scala is here.
The author uses call-by-name parameters to implement control abstraction.



XML Literals

No (may be in future?)

XML literals are supported.

object Main {
def main(args: Array[String]): unit = {
var s = <html>
<title>{args(0)}
</title></html>;

Console.println(s);
}
}

Possible to mix Scala expressions in XML literals - as shown
in above example (within {} args[0] is referred). XML elements
may be used inside pattern-matching as well (only elements).


See also: Scala XML Processing,
<scala/xml>

Join the discussion

Comments ( 5 )
  • S. Micheloud Wednesday, March 7, 2007

    About import statements: renaming is possible inside import selectors (see Scala reference, section 4.7, p. 40ff); for example:

    object Main extends Application {import java.util.{BitSet => JBitSet}import scala.collection.mutable.{BitSet => MBitSet}import scala.collection.immutable.BitSetval set1 = new JBitSet(8)val set2 = new MBitSet(8)val set3 = new BitSet(8, 8, Array(4), false)
    set1.set(2)
    set2 += 2
    Console.println("set1 = " + set1) // set1 = {2}
    Console.println("set2 = " + set2) // set2 = Set(2)
    Console.println("set3 = " + set3) // set3 = Set(2)
    }

  • A. Sundararajan Wednesday, March 7, 2007
    S. Micheloud: Thank you for that info - I'm still learning Scala (I started very recently). Thanks again!
  • Krishnan Wednesday, March 7, 2007
    Very interesting blog post. Will definitely look up Scala.
  • S. Micheloud Friday, March 9, 2007

    About throwing exceptions: Java programmers using Scala code can be forced to have "throws" clauses in their code using a Scala annotation; for example:

    // File: Reader.scala
    class Reader(fname: String) {import java.io._private val in = new BufferedReader(new FileReader(fname))
    @throws(classOf[IOException])def read() = in.read()
    }
    // File: Test.java
    import java.io.IOException;
    public class Test {public static void main(String[] args) { // throws IOException {
    Reader rd = new Reader("Test.java");int ch;while ((ch = rd.read()) != -1) {
    System.out.print((char) ch);
    }
    }
    }

    Compiling Test.java will produce the following error message:

    Test.java:6: unreported exception java.io.IOException; must be caught or declared to be thrown
    while ((ch = rd.read()) != -1) {
    \^
    1 error

    Simply uncomment the "throws" clause to compile and run the example !

  • A. Sundararajan Friday, March 9, 2007
    S. Micheloud: Thanks again! BTW, did you have chance to look at my (next) post on Scala singleton posts. Could you please clarify or point me to the right reference?
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.