Feature
|
Java
|
Scala
|
Static Typing
|
Yes
|
Yes
|
Object Oriented programming
|
Yes
|
Yes
|
Functional programming
|
No
|
Yes
|
Variable declaration
|
// type var_name = init_value;
int i = 0;
|
// var var_name: type = init_value;
var i : int = 0;
|
Constant declaration
|
// final type var_name = init_value;
final int i = 0;
|
// val var_name: type = init_value;
val i : int = 0;
|
Class declaration
|
class Person {
// members here
}
|
class Person {
// members here
}
|
Methods
|
// RetType name(PType1 pName1, PType2 pName2...)
class Person {
// members here
public String getName() {
// code here..
}
public void setAge(int age) {
// code here..
}
}
|
// def name(pName: PType1, pName2: PType2...) : RetType
class Person {
// members here
def getName() : String = {
// code here..
}
def setAge(age: int) : unit = {
// code here..
}
}
|
Constructors
|
Method(s) with same name as class name with no return type.
class Person {
public Person(String name, int age) {
// initialization here..
}
public Person(String name) {
// call the other constructor
this(name, 1);
}
}
|
Constructor parameters are specified in class declaration itself.
Example:
class Person(name: String, age: int) {
// any method can access "name" or "age" parameter
}
You can have "secondary constructors" as well. Example:
class Person(name: String, age: int) {
// any method can access "name" or "age" parameter
def this(name: String) {
// call the "primary" constructor
this(name, 1);
}
}
|
Operator overloading
|
No. Except that string concatenation can be done using "+" - which gets
transformed as "concat" calls.
|
Yes. Just use operator as method name. Example:
class Complex {
def + (other: Complex) : Complex = {
//....
}
}
Also, any single parameter method can be used as an infix operator.
Example:
// call System.exit(int) using infix notation
System exit 0
// call Thread.sleep(int) using infix notation
Thread sleep 10
See also: Scala operators
|
Static fields and methods
|
class Person {
private static Person president = ....
private static Person getPresident() {
return president;
}
}
|
No static members. Use singletons.
|
Singletons
|
No language support. You can simulate something like this:
class President extends Person {
// make the constructor private - so that
// instance can not be created outside of this class
private President() {}
// create the singleton object here..
private static President thePresident = new President();
// provide accessor for singleton
// and add other methods here..
}
|
object President extends Person {
// have methods of the singleton object
}
Singleton "objects" are in effect "modules" in Scala. Also, every Java class
is viewed like any other Scala class without static methods and fields
and a singleton object [whose name is same as Java class name] that contains
only the static methods and fields of Java class as it's (non-static) members.
This allows accesing static methods of Java classes but at the same time
not having static members in Scala.
|
Inheritance
|
class Person {
}
class Graduate extends Person {
}
|
class Person {
}
class Graduate extends Person {
}
See also: Scala subclassing.
But, this reference needs update. Now, Java (since JDK 5.0) supports
covariant return types.
|
Calling super class constructor
|
class Graduate extends Person {
public Graduate(String name, int age, String degree) {
super(name, age);
}
}
|
You call super class constructor in class declaration itself.
class Graduate(name: String , age: int, degree: String)
extends Person(name, age) {
}
|
Method Overriding
|
class Graduate extends Person {
@Override public String getName() {
// code here...
}
}
@Override is optional - but helps with detecting errors (you may be thinking
you are overriding - but in fact you may actually be overloading a super
class method).
|
class Graduate extends Person {
override def getName(): String = {
// code here...
}
}
The keyword "override" is mandatory for non-abstract method overrides.
|
Abstract classes and methods
|
abstract class Person {
abstract public String getName();
}
|
abstract class Person {
def getName(): String;
}
No "abstract" keyword for methods. Only for classes.
|
Root of all reference types
|
java.lang.Object
|
scala.AnyRef
- which is same as java.lang.Object in JVM implementation of Scala
|
Uniform object orientation? (i.e., is everything object?)
|
No. There are primitive types and reference types. But with
autoboxing/unboxing,
you don't need to explicitly convert b/w primitive types and corresponding box types.
|
Yes. "int" is an alias to "scala.Int" and so on. A bit explanation of
type hierarchy. scala.Any is supertype of all types. scala.AnyVal is super
type of value types (such as "int" etc.). scala.AnyRef is super type of
all "reference" types. But, method calls etc. work on value types as well.
Examples:
- 44.+(4) is same as 44 + 4 where "+" is a method on scala.Int class.
-
Also, single argument methods can be called using "infix" notation just like
operator methods are. Example:
System exit 4 is same as
System.exit(4)
|
interfaces
|
interface Runnable {
void run();
}
|
Use traits. Traits are like interface but can have method bodies (i.e., not just
method declarations). See also: multiple inheritance.
trait Runnable {
def run(): unit;
}
|
Multiple inheritance
|
No. Only multiple interfaces may be implemented. Only single class may
be extended.
|
Multiple classes can not be extended. Multiple traits can be.
And traits can have code -- not just declarations.
trait Runnable {
// parameter accepting run method
def run(obj: AnyRef) : unit;
// no params - assuming "null" instead
def run() : unit = {
System.out.println("assuming null...");
run(null);
}
}
// extend a single class, but can "extend"
// multiple traits with "with"
class MyClass extends AnyRef with Runnable {
// just implement run with param.
// Other "run" is inherited from Runnable
def run(obj: AnyRef) : unit = {
System.out.println("got " + obj);
}
}
See also: Scala mixins,
mixins paper,
traits paper.
|
Inner classes
|
public class Book {
private String name;
// ...
public Order newOrder() {
...
}
public class Order {
private Date orderDate;
private int quantity;
// ...
}
}
|
class Book(name: String) {
class Order(orderDate: Date,
quantity : int) {
}
def newOrder() : Order {
return new Order(new Date(), 1);
}
}
Unlike Java where such inner classes are members of the enclosing class, in Scala
inner classes are bound to the outer object. With the above classes, in Java you
can write
// Java
Book freakonomics = new Book("Freakonomics");
Book letUsKillGandhi = new Book("Let us kill Gandhi");
Book.Order o1 = freakonomics.newOrder();
Book.Order o2 = letUsKillGandhi.newOrder();
// You can assign any Book.Order
// to any other Book.Order
o2 = o1;
// Not in Scala!
val freakonomics: Book = new Book("freakonomics");
val letUsKillGandhi : Book = new Book("Let us kill Gandhi");
var o1 = freakonomics.newOrder();
var o2 = letUsKillGandhi.newOrder();
// this line below will not compile!
// type of "o1" is "freakonomics.Order"
// and type of "o2" is "letUsKillGandhi.Order"
o2 = o1;
In other words, order for "Freakonomics" book can not be treated
as instance of order for "Let us kill Gandhi"! If you really want to refer
to inner class type as in Java, you can use the following:
var o1 : Book#Order = freakonomics.newOrder();
var o2 : Book#Order = letUsKillGandhi.newOrder();
o1 = o2;
See also: Scala inner classes.
|
Class literals
[section 15.8.2 of JLS].
|
String.class
Object.class
|
classOf[String]
classOf[AnyRef]
See also: Scala classOf.
|
Dynamic type check
|
x instanceof String
n instanceof Number
|
x.isInstanceOf[String]
n.isInstanceOf[Number]
|
Dynamic type cast
|
String x = (String) obj;
Number n = (Number) obj;
|
x : String = obj.asInstanceOf[String];
n : Number = obj.asInstanceOf[Number];
|
Generics
|
// an interface with a type parameter
interface Stack<E> {
void push(T t);
T pop();
boolean isEmpty();
}
// a class with two type parameters
class Pair<K, V> {
private K key;
private V value;
public Pair(K key, V value) {
this.key = key;
this.value = value;
}
// more code...
}
|
// a trait with a type parameter
trait Stack[E] {
def push(o: E) : unit;
def pop() : E;
def isEmpty(): boolean;
}
// a class with two type parameters
class Pair[K, V](key: K, value: V) {
// code here...
}
See also: Scala generics.
|
Generic methods
|
class Test {
public <T> List<T> dup(T t, int n) {
//....
}
}
|
class Test {
def dup[T](x: T, n: Int): List[T] = {
if (n == 0) Nil
else x :: dup(x, n - 1)
}
}
See also: Scala polymorphic methods.
|
Bounded type parameter
|
// upper bound
class Foo<E extends SomeType> {
// ... code here
}
// lower bound
class Foo<E super SomeType> {
// ...
}
// type parameter in bound
class Stack<E extends Comparable<E>> {
// ...
}
|
// upper bound
class Foo[E <: SomeType] {
// ...
}
// lower bound
class Foo[F >: SomeType] {
// ...
}
// type parameter in bound
class Stack[E extends Comparable[E]] {
// ...
}
|
Wildcards
|
// upper bound
ArrayList<? extends Number> l = ...
// lower bound
ArrayList<? super Number> l = ...
// wildcard without bound
List<?> list = ...
See also: Wildcards paper
|
In Java, variance annotations are specified by "clients" [a.k.a use-site]. In
Scala, if you want Foo[A] to be subtype of Foo[B] whenever A is subtype of B,
you have to declare Foo as
// annotation +T declares type T to be used
// only in covariant positions.
class Foo[+A] {
// ...
}
// annotation -T declares type T to be used
// only in contravariant positions.
class Foo[-A] {
}
Example: In Scala, Lists are immutable and List has covariant
annotation. So, List[B] is subtype of List[A] whenever B is
subtype of A.
See also: Scala variances
|
Arrays
|
String[] s = new String[10];
|
var s : Array[String] = new Array[String](10);
Array is a generic type in Scala (much like "Vector", "List" etc.
in Java are). Note: Arrays do not follow the covariant subtype rule.
i.e., Array[Graduate] is not a subtype of Array[Person]
even if Graduate is subtype
of Person - this is unlike Java language in which
Graduate[] is a subtype of Person[] .
|
Array element access, update
|
int[] a = new int[3];
a[0] = 3;
System.out.println(a[0]);
|
var a : Array[int] = new Array[int](3);
a(0) = 3;
System.out.println(a(0));
Array element access and update are actually method
calls on Array[T]. You may be wondering how method call
appears on the right side (for element update). No,
Scala does not have C++ style reference (&) types.
a(i) = 10 is translated as a.update(i, 10)
|
Varargs
|
class Calc {
public static int sum(int... values) {
int res;
for (int i in values) {
res += i;
}
return res;
}
}
// with the above definition, caller can use
Calc.sum(3, 44)
Calc.sum(4, 4, 5,45, 45);
With '...' after type of last parameter, your method becomes variadic. The type
of "values" in above example is int[] . In general, the
type would be T[] , if the last param type was T... .
|
object Calc {
def sum(values: int\*): int = {
var res: int = 0;
for (val v <- values) {
res = res + 1;
}
return res;
}
}
// With the above definition, caller can use
Calc.sum(3, 44);
Calc.sum(4, 5, 6, 6);
With '\*' for the last parameter, your method becomes variadic. The type
of "values" in above example is Seq[int] . In general, the
type would be Seq[T] , if the last param type was T\* .
|
Type inference
|
Only for generic methods (
possible future improvements?)
|
Supported everywhere possible. Examples:
- You can leave types for in var and val. Based on initial value
type is inferred.
- You can leave return type for non-recursive methods as well.
Based on return expression return type is inferred.
// int type for "i" inferred
val i = 10;
// String type for "s" inferred
var s = "hello"
// "int" return type inferred
def add(i: int, j:int) = i \* j
// "unit" return type inferred
def sayHello = System.out.println("hello world")
// scala.List[java.lang.String] inferred
var v = List("hello", "world");
// with class Pair[K, V](k: K, v: V) {...}
// Pair[int, String] is inferred for the following.
var p = new Pair(2, "hello");
See also: Scala type inference.
|
Functions, anonymous functions and closures
|
Not yet. May be in future?.
|
Yes. Can define functions, anonymous functions and closures
anywhere -- except in top-level scope (compilation unit level).
// function
def add(i: int, j: int) = i + j
// functions are first-class objects
// accepts a callback function as parameter
def oncePerSecond(callback: ()=>unit): unit = {
while (true) { callback(); Thread.sleep(1000);
}
def printHello() = {
System.out.println("hello world");
}
// pass printHello as parameter
oncePerSecond(printHello);
// define anonymous function and call it
((i:int, j:int) => i+j)(3, 4)
// anonymous function passed as parameter
oncePerSecond(()=>System.out.println("hello"));
// nested functions too
def outer() = {
def inner() = {
System.out.println("I am inner");
}
inner();
}
// inner functions can access outer's locals
// and arguments
def outer(s: String) = {
def inner() = {
System.out.println("outer's 's': " + s);
}
inner();
}
// Java closure proposals talk about converting
// closures to interfaces automatically. I think
// we can use views in Scala.
// define conversion from any parameterless function
// to java.lang.Runnable
implicit
def asRunnable(func : ()=>unit) : Runnable = {
new Runnable() {
def run() {
func()
}
}
}
def main ( args : Array[String] ) = {
// create a new thread -
// passing an anonymous function
// for Runnable
var t = new Thread(()=>Console.println("hello"));
t.start();
// you can now initialize Runnable
// with any function
var r : Runnable = ()=>Console.println("I am running!")
r.run();
}
See also: Scala views.
|
List support
|
Use java.util.List
and classes implementing it.
|
// create a List with "List" function (which is actually
// an object with "apply")
var names : List[String] = List("Java", "JavaScript")
// :: is the cons operator
// creates a new list with first element as "Scala"
// and rest of the elements from "names" list
val l = "Scala" :: names
// :: becomes method call on List -- unlike
// other operators :: takes right-side value
// as list as target object to call method on!
Lists in Scala are immutable. There are methods
on List class - like map, filter etc. that accept
closure arguments. Examples:
// make a new list that contains only short names
// shortNames is of type List[String]
var shortNames = names.filter(n=>n.length() < 6)
// make a new list that has lengths of names
// "lengths" is of type List[int]
var lengths = names.map(n=>n.length())
scala.List has covariant type parameter [note: Lists are immutable].
So, List[Graduate] is subtype of List[Person]
if Graduate is subtype of Person .
val graduates:List[Graduate] =...
val persons:List[Person] = graduates;
|
There is much more to Scala - pattern matching, for-comprehension, exception handling, packages, abstract types, XML literals, currying, implicit parameters etc. But, we have had enough for a single blog entry
Posted by Someone on March 06, 2007 at 01:26 AM IST #
Posted by Vaibhav on March 06, 2007 at 06:05 AM IST #
Posted by A. Sundararajan on March 06, 2007 at 08:42 AM IST #
Posted by Vaibhav Choudhary on March 06, 2007 at 09:01 AM IST #
Posted by A. Sundararajan on March 06, 2007 at 09:13 AM IST #
Posted by Robert on March 06, 2007 at 05:30 PM IST #
Posted by David Pollak on March 06, 2007 at 06:43 PM IST #
Thanks - really great summary!
Another great thing about Scala, not explicitly mentioned above, is the fact that Scala integrates really nicely with Java. You can implement Java interfaces, extend Java classes, call Java methods, and so on.
An example on the Scala website shows how natural this is.
Posted by Rich Dougherty on March 07, 2007 at 03:20 AM IST #
Posted by guest on March 07, 2007 at 06:57 PM IST #