In this series, we’ve already
laid the foundations for Scala’s syntax as well as gotten a feel for how some of its
object-oriented constructs
work. We haven’t really looked at any of these subjects in-depth
however. Most of our effort has been focused on high-level, flash bang
overviews that just get you tasting your way into the language. This
post will go into greater depth regarding method syntax, touch a bit on
scopes and attempt to cover how static members work in Scala. We will
also touch on a few gotchas due to “missing” imperative instructions.
Methods Galore
Scala isn’t called a “functional language” just because it’s
Turing complete.
Scala has a very powerful and flexible syntax as it relates to methods,
both declaration and invocation. We’ve already seen some basic
samples:
| class Person {
def firstName() = {
var back:String = ... // read from a database
back
}
}
|
Fairly straightforward. But this doesn’t really give you the full
picture. In Java for example, you can create methods with different
visibilities, modifiers and (oddly enough) return types. Does Scala
support all of this flexibility?
The answer is a qualified “yes”. Scala does allow for different
visibilities on not just methods, but any members. For example:
| class Person {
private var name = "Daniel Spiewak"
val ssn = 1234567890 // public constant field
def firstName() = splitName()(0) // public method
private def splitName() = name.split(" ") // private method
protected def guessAge() = {
import Math._
round(random * 20)
}
}
|
At the risk of going on a tangent, it’s worth pointing out the (seemingly) out of place
import statement within the
guessAge()
method. I mentioned in the first post that Scala’s import is far more
powerful than Java’s. One of its many charms is imparting to the power
to import into a specific scope. The
import statement within
guessAge() is much like a Java
static import statement which only provides access to the
Math members within the
guessAge() method. So we couldn’t just make a call to
round() from within the
splitName() method. Rubyists can think of it much like the
include
statement without all of the hassle (it’s not actually including, it’s
importing and so eliminating the need for fully-qualified names).
Scala access modifiers are also quite a bit more powerful than Java’s. For example,
protected
by default limits access to only subclasses, unlike Java which also
allows access to other classes in the same package. More importantly
though, Scala allows the developer to more explicitly specify the scope
of the access modifier. This is accomplished using the
modifier[package] notation. For example:
| package com.codecommit.mypackage
class MyClass {
private[mypackage] def myMethod = "test"
}
|
In this example,
myMethod is access-restricted to both the enclosing class
and the enclosing package. Essentially, this is how the Java-style package private modifier can be emulated using Scala. The
protected
modifier also allows such visibility qualifiers. The one restriction
here is that the package specified must be an enclosing package. In the
above example, specifying
private[com.codecommit.mypackage] is perfectly valid, but
private[scala.collection.immutable] would not be correct.
So with the exception of package-private, visibilities work about the
same in Scala as they do in Java, both in syntax and function.
Modifiers are really where things get interesting. Scala has far fewer
method modifiers than Java does, primarily because it doesn’t
need so many. For example, Scala supports the
final modifier, but it doesn’t support
abstract, native or
synchronized:
| abstract class Person {
private var age = 0
def firstName():String
final def lastName() = "Spiewak"
def incrementAge() = {
synchronized {
age += 1
}
}
@native
def hardDriveName():String
}
|
If we were in Java, we would write the above like this:
| public abstract class Person {
private int age = 0;
public abstract String firstName();
public final String lastName() {
return "Spiewak";
}
public synchronized void incrementAge() {
age += 1;
}
public native String hardDriveAge();
}
|
Yes, I know it’s more “Scala-esque” to use actors rather than
synchronized(), but one step at a time.
You see how Scala keeps with its theme of making the common things
concise? Think about it, almost every method you declare is public, so
why should you have to say so explicitly? Likewise, it just makes sense
that methods without a body should be implicitly abstract (unless
they’re native).
One very important type-saving feature that you should see in the example above is that Scala doesn’t
force
you to declare the return type for your methods. Once again the type
inference mechanism can come into play and the return type will be
inferred. The exception to this is if the method can return at
different points in the execution flow (so if it has an explicit return
statement). In this case, Scala forces you to declare the return type
to ensure unambiguous behavior.
You should also notice that none of the Scala methods actually include a
return statement. This of course seems odd as judging by the Java translation,
lastName() should return a
String.
Well it turns out that Scala carries a useful shortcut for method
returns: the last statement in an expression, be it a scope, a closure
or a method becomes its return value. This convention is also found in
languages like Ruby and Haskell. This example illustrates:
| def name() = {
val name = new StringBuilder("Daniel")
name.append(" Spiewak");
name.toString()
}
val s = name()
println(s) // prints "Daniel Spiewak"
|
Again, in this example the return type of the method is inferred (as
String). We could just as easily have written the
name() method as follows, it just would have been less concise.
| def name():String = {
val name = new StringBuilder("Daniel")
name.append(" Spiewak");
return name.toString()
}
|
This “returnless” form becomes extremely important when dealing with
anonymous methods (closures). Obviously you can’t return from a
closure, you can only
yield, however the principle is the
same. Since closures are often used to reduce code bulk and make
certain algorithms more concise, it only makes sense that their return
syntax would be as compact as possible:
| val arr = Array(1, 2, 3, 4, 5)
val sum = arr.reduceLeft((a:Int, b:Int) => a + b)
println(sum) // 15
|
In this example we’re passing an anonymous method to the
reduceLeft() method within
Array. This method just calls its parameter function repeatedly for each value pair, passing them as the
a and
b parameters. Here’s the key part though: our anonymous method adds the two parameters and yields the result back to
reduceLeft(). Again, no
return statement (or actually, as a closure it would be
yield). Also, we don’t explicitly specify the return type for the closure, it is inferred from our last (and only) statement.
Method Overriding
A very important concept in object-oriented programming is method
overriding, where a subclass redefines a method declared in a
superclass. Java’s syntax looks like this:
| public class Fruit {
public int getWorth() {
return 5;
}
}
public class Apple extends Fruit {
@Override
public int getWorth() {
return 1;
}
}
|
Technically, the
@Override annotation is optional, but it’s
still good practice to use it. This gives you the compile-time
assurance that you actually are overriding a method from a superclass.
In principle, a method declared in a subclass overrides any method in
the superclass declared with the exact same signature. At first glance
this seems great, less syntax right? The problem is when you start
dealing with APIs where you’re uncertain if you got the overriding
method signature right. You could just as easily overload the method
rather than overriding it, leading to totally different functionality
and sometimes hard-to-trace bugs. This is where
@Override comes in.
Scala actually has a bigger problem with method overriding than just
signature verification: multiple inheritance. Multiple inheritance is
when one class inherits from more than one superclass. C++ had this
feature years ago, effectively demonstrating how
horrible it can really be.
When Gosling laid out the initial spec for Java, multiple inheritance
was one of the things specifically avoided. This is good for
simplicity, but it’s sometimes constraining on the power-end of life.
Interfaces are great and all, but sometimes they just don’t cut it.
The key to avoiding ambiguities in the inheritance hierarchy is explicitly stating that a method
must override a superclass method. If that method has the same signature as a superclass method but doesn’t
override it, a compile error is thrown. Add to that significant ordering in the
extends/with clauses, and you get a workable multiple-inheritance scheme. But I’m getting ahead of myself…
Here’s the
Fruit example, translated into Scala:
| class Fruit {
def worth() = 5
}
class Apple extends Fruit {
override def worth() = 1
}
|
Notice that in Scala,
override is actually a keyword. It is
a mandatory method modifier for any method with a signature which
conflicts with another method in a superclass. Thus overriding in Scala
isn’t implicit (as in Java, Ruby, C++, etc), but explicitly declared.
This little construct completely solves the problems associated with
multiple inheritance in Scala. We’ll get into
traits and multiple inheritance in more detail in a future article.
Often times when you override a method, you need to call back to the
superclass method. A good example of this would be extending a Swing
component:
| class StrikeLabel(text:String) extends JLabel(text) {
def this() = this("")
override def paintComponent(g:Graphics):Unit = {
super.paintComponent(g)
g.setColor(Color.RED)
g.drawLine(1, getHeight() / 2, getWidth() - 1, getHeight() / 2)
}
}
|
This component is just a rack-standard
JLabel with a red
line drawn through its center. Not a very useful component, but it
demonstrates a pattern we see used a lot in Java: delegating to the
superclass implementation. We don’t want to actually implement all of
the logic necessary to paint the text on the
Graphics context with the appropriate font and such. That work has already been done for us in
JLabel. Thus we ask
JLabel to paint itself, then paint our
StrikeLabel-specific logic on top.
As you see in the example, the syntax for making this superclass
delegation is almost precisely the same as it is in Java. Effectively,
super is a special private value (much like
this) which contains an internal instance of the superclass. We can use the value just like
super in Java to access methods and values directly on the superclass, bypassing our overriding.
That little bit of extra syntax in the
extends clause is how you call to a superclass constructor. In this case, we’re taking the
text parameter passed to the default constructor of the
StrikeLabel class and passing it on to the constructor in
JLabel. In Java you do the same thing like this:
| public class StrikeLabel extends JLabel {
public StrikeLabel(String text) {
super(text);
}
public StrikeLabel() {
this("");
}
}
|
This may seem just a bit odd at first glance, but actually provides a
nice syntactical way to ensure that the call to the super constructor
is
always the first statement in the constructor. In Java,
this is of course compile-checked, but there’s nothing intuitively
obvious in the syntax
preventing you from calling to the super
constructor farther down in the implementation. In Scala, calling the
super constructor and calling a superclass method implementation are
totally different operations, syntactically. This leads to a more
intuitive flow in understanding why one can be invoked arbitrarily and
the other must be called prior to anything else.
Scala’s Sort-of Statics
Scala is a very interesting language in that it eschews many of the
syntax constructs that developers from a Java background might find
essential. This ranges from little things like flexible constructor
overloading, to more complex things like a complete
lack of static member support.
So in Java, static members are just normal class members with a
different modifier. They are accessed outside of the context of a
proper instance using the classname as a qualifier:
| public class Utilities {
public static final String APP_NAME = "Test App";
public static void loadImages() {
// ...
}
public static EntityManager createManager() {
// ...
}
}
System.out.println(Utilities.APP_NAME);
Utilities.loadImages();
EntityManager manager = Utilities.createManager();
|
Scala does support this type of syntax, but under the surface it is quite a bit different. For one thing, you don’t use the
static
modifier. Instead, you declare all of the “static” members within a
special type of class which acts as an only-static container. This type
of class is called
object.
| object Utilities {
val APP_NAME = "Test App"
def loadImages() = {
// ...
}
def createManager():EntityManager = {
// ...
}
}
println(Utilities.APP_NAME)
Utilities.loadImages()
val manager = Utilities.createManager()
|
The syntax to use these “statics” is the same, but things are quite a bit different in the implementation. It turns out that
object actually represents a singleton class.
Utilities
is in fact both the classname and the value name to access this
singleton instance. Nothing in the example above is static, it just
seems like it is due to the way the syntax works. If we port the above
class directly to Java, this is what it might look like:
| public class Utilities {
private static Utilities instance;
public final String APP_NAME = "Test App";
private Utilities() {}
public void loadImages() {
// ...
}
public EntityManager createManager() {
// ...
}
public static synchronized Utilities getInstance() {
if (instance == null) {
instance = new Utilities();
}
return instance;
}
}
// ...
|
So Scala provides a special syntax which basically gives us a
singleton for free, without all of the crazy syntax involved in
declaring it. This is a really elegant solution to the problems with
proper statics. Since Scala doesn’t actually have static members, we no
longer have to worry about mixing scopes, access qualifiers, etc. It
all just works nicely.
But what about mixing static and instance members? Java allows us to do this quite easily since
static is a qualifier, but Scala requires “static” members to be declared in a special singleton class. In Java, we can do this:
| public class Person {
public String getName() {
return "Daniel";
}
public static Person createPerson() {
return new Person();
}
}
|
The solution in Scala is to declare both an
object and a
class of the same name, placing the “static” members in the
object and the instance members in the
class. To be honest, this seems extremely strange to me and is really the only downside to Scala’s singleton syntax:
| object Person {
def createPerson() = new Person()
}
class Person {
def name() = "Daniel"
}
|
The syntax for using this
object/class combination is
exactly the same as it would be in Java had we mixed static and instance
members. The Scala compiler is able to distinguish between references
to
Person the
object and references to
Person the
class. For example, the compiler knows that we can’t create a new instance of an
object, since it’s a singleton. Therefore we must be referring to the
class Person in the
createPerson() method. Likewise, if a call was made to
Person.createPerson(), the compiler is more than capable of deducing that it must be a reference to the
object Person
as there is no way to access a method directly upon a class. It’s all
perfectly logical and consistent, it just strikes the eye funny when you
look at it.
Conclusion
And so ends our two part, whirlwind tour of Scala’s object-oriented
constructs, methods and statics. There are of course trivialities along
the way which we haven’t covered, but those are easy enough to learn
now that you have the basics. The more interesting syntax is still to
come though. For one thing, we’ve barely scratched the surface of all
of the things that you can do with methods. They don’t call it a
“functional” language for nothing! But in keeping with our goal to
represent the imperative side of the Scala language, we’ll save that for
later.
(codecommit)
0 comments: on "Scala for Java Refugees Part 3: Methods and Statics"
Post a Comment