Scala Classes and Inheritance (2019)

Scala Classes and Inheritance

Scala Classes, Inheritance, and Abstraction

Inheritance is one of the most powerful features of Object Orientation. It is the difference between an encapsulated language that provides an object-based model and an Object-Oriented language.

 

Inheritance is also one of the main tools sup-porting reuse in an Object-Oriented language (although in Scala’s case Traits are also a major tool for reuse). You will use inheritance all the time without even realizing it, indeed you have already been doing so every time you have benefited from the default implementation of toString.

 

Scala has single class inheritance although it does have a form of multiple inheritances via Traits.

 

What Are Classes for?

In some Object-Oriented languages, classes are merely templates used to construct objects (or instances). In these languages, the class definition specifies the structure of the object and a separate mechanism is often used to create the object using this template.

 

In some other languages (e.g. Java, C#, Smalltalk), classes are objects in their own right; this means that they can not only create objects, they can also hold data, receive messages and execute methods just like any other object.

 

However, many programmers find this distinction confusing and Scala has adopted the Companion object approach instead which means that a class is supported by a singleton object that can hold data and provide a placeholder for additional supporting behaviors.

 

Thus, in Scala, classes are unique within a program and can:

  • defined using the keyword class,
  • be used to create instances (via the keyword new),
  • be inherited by subclasses (and can inherit from existing classes),
  • mix in traits,
  • define properties,
  • define methods,
  • define functions,
  • define instance variables and values,
  • be sent messages.
  • Objects, on the other hand, are:
  • defined using the keyword object,
  • singleton entities within the systems,
  • cannot be instantiated (and do not support the new operation),
  • cannot be used to create new instances,
  • are accessed directly via their name rather than via any val or var.

 

Confusingly many Object-Oriented languages use the term object to refer to an instance of a class. In Scala an instance of a class is exactly that, an instance of a class, an object is a different concept. Instances can be

  • created from a class (using the keyword new),
  • hold their own copy of their state (in terms of properties or instance variables),
  • be sent messages,
  • execute instance methods,
  • execute functions,
  • have many copies in the system (all with their own data).

 

Inheritance Between Types

Scala Inheritance

To recap on the concept of inheritance, inheritance is supported between types within Scala. For example, a class and extend (subclass) another class. A trait (another type) can extend other traits, etc., and objects can extend traits or classes. All of these types of support and enable inheritance.

In terms of the inheritance we say:

  • A subtype inherits from a supertype
  • A subtype obtains all code and data from the supertype
  • A Subtype can add new code and data
  • A subtype can override inherited code and data
  • A subtype can invoke inherited behavior or access inherited data.

 

Inheritance Between Classes

Inheritance is achieved in Scala using the extends keyword. Scala is a single class inheritance system, so a Scala class can only inherit from a single class (although it can mix in multiple traits, to be discussed later).

 

The following class definition builds on the class Person presented earlier:

class Person(val name: String, var age: Int)

class Student(var subject: String,
String,
Int)
extends Person(n, a) {
}

This class extends the class Person by adding a new variable property, subject. As this is a var the class Student also provides reader and writer functionality for the subject property. We say that the Student is a subclass of Person and that Person is the superclass of Student.

 

Note that as the class Person defined two properties in its primary constructor, the class Student must invoke the constructor explicitly. It does this by indicating the data to pass to this constructor after the parent class name following the extends expression. For the student class, we take these values in as part of its own constructor.

 

However, the parameters ‘n’ and ‘a’ are not properties; they are local fields which can be used within the definition of the class Student. We are only using them to pass the data up to the definition of the constructor in the class Person.

 

As a result, you can only instantiate the class Student by providing the subject to be studied, the student’s name and their age. For example,

object StudentTest extends App {

val s = new Student("Computer Science", "John", 18)

println(http://s.name + " is studying " + s.subject)

}

 

The end result is that a new instance of the class Student is created that has a subject property and also a name property and an age property inherited from the class Person. In fact, the instance referred to by the variables is a Student and is also a Person (in the same way that any human is also a Mammal, etc.).

 

Note that it is necessary to invoke a parent class’s constructor explicitly. The only exceptions to this are if the parent class only defines a Zero parameter constructor, or if the primary constructor provides default values for all of its parameters.

 

The Role of a Subclass

There are only a small number of things that a subclass should do relative to its parent or superclass. If a proposed subclass does not do any of these, then your selected parent class is not the most appropriate superclass to use.

 

A subclass should modify the behavior of its parent class or extend the data held by its parent class. This modification should refine the class in one or more of these ways:

  • Changes to the external protocol, the set of messages to which instances of the class respond.
  • Changes in the implementation of the methods, i.e. the way in which the messages are handled.
  • Additional behavior that references inherited behavior.

 

If a subclass does not provide one or more of the above, then it is incorrectly placed. For example, if a subclass implements a set of new methods, but does not refer to the instance variables or methods of the parent class, then the class is not really a subclass of the parent (it does not extend it).

 

As an example, consider the class hierarchy. A generic root class has been defined. This class defines a Conveyance which has doors, fuel (both with default values) and a method, startUp, that starts the engine of the conveyance.

 

Three subclasses of Conveyance have also been defined: Dinghy, Car and Tank. Two of these subclasses are appropriate, but one should probably not inherit from Conveyance. We shall consider each in turn to determine their suitability.

 

The class Tank overrides the number of doors inherited, uses the startup method within the method of fire and provides a new instance variable. It, therefore, matches all three of our criteria.

 

Similarly, the class Car overrides the number of doors and uses the method startUp. It also uses the instance variable fuel within a new method to accelerate. It also, therefore, matches our criteria.

 

The class Dinghy defines a new instance variable sails and a new method detail. As such, it does not use any of the features inherited from Conveyance. However, we might say that it has extended Conveyance by providing this instance variable and method. We must then consider the features provided by Conveyance.

 

We can ask ourselves whether they make sense within the context of Dinghy. If we assume that a dinghy is a small sail-powered boat, with no cabin and no engine, then nothing inherited from Conveyance is useful.

 

In this case, it is likely that Conveyance is misnamed, as it defines some sort of a motor vehicle, and the Dinghy class should not have extended it.

 

The exceptions to this rule are subclasses of Any and AnyRef. This is because these classes are the root types in the Scala type hierarchy. AnyRef is the root of all reference types—that is classes in Scala. As you must create a new class by sub-classing it from an existing class, you can subclass from AnyRef when there is no other appropriate class.

 

Capabilities of Classes

A subclass or class should accomplish one specific purpose; it should capture only one idea. If more than one idea is encapsulated in a class, you may reduce the chances for reuse, as well as contravene the laws of encapsulation in Object-Oriented systems.

 

For example, you may have merged two concepts together so that one can directly access the data of another. This is rarely desirable.

 

Breaking a class down costs little but may produce major gains in reusability and flexibility. If you find that when you try and separate one class into two or more classes, some of the code needs to be duplicated for each class, then the use of abstract classes can be very helpful. That is, you can place the common code into an abstract superclass to avoid unnecessary duplication.

 

The following guidelines may help you to decide whether to split the class with which you are working. Look at the comment describing the class (if there is no class comment, this is a bad sign in itself). Consider the following points:

 

Is the comment short and clear? If not, is this a reflection on the class? Consider how the comment can be broken down into a series of short clear comments. Base the new classes around those comments.

 

If the comment is short and clear, do the class and instance variables make sense within the context of the comment? If they do not, then the class needs to be re-evaluated. It may be that the comment is inappropriate, or the class and instance variables inappropriate.

 

Look at the instance variable references (i.e. look at where the instance variable access methods are used). Is their use in line with the class comment? If not, then you should take appropriate action.

 

Overriding Behaviour

As was mentioned at the start of this blog, a subtype (e.g. a subclass) can override the behavior defined in a parent class. In fact, it is possible to override both methods and fields.

 

It should be noted that in Scala; it is also possible to override a parameterless method by a new field or property (this is actually to do with the way in which Scala internally represents data and methods) but can be useful and also confusing.

 

To override either a field or a method in a parent class you must use the keyword override. You have seen this already with the toString method where we had to include the keyword override in order to redefine toString to do something more useful then display the fully qualified class name and a hexadecimal number.

 

Of course, the default behavior of toString was being inherited into our classes via the class AnyRef (which we implicitly extended).

 

In the following example, the class Base overrides toString so that the name and age properties of the Base class are used to create the string representation of instances of the class. It also defines a method max and property working.

class Base(val name: String, var age: Int) { def max(x: Int, y: Int): Int = if (x > y) x else y val working = false
override def toString() = s"$name is $age"
}
We can then subclass Base with the class Derived and override both max and working if we wish, for example,
class Derived(n: String, a: Int) extends Base(n, a) {
override def max(x: Int, y: Int): Int =
if (x > y) y else x
override val working = true
}

 

In Derived we have redefined max to actually return the minimum value for some reason and overridden working to be true.

 

As another option consider the classes Cat and Tiger below. Cat has values dangerous and name. Tiger overrides dangerous and name. However, the value for the name is now set when the instance is created.

 

Thus the property that is defined as part of the constructor overrides a property used with the Cat class, which was not originally part of any construction process.

class Cat {
val dangerous = false
val name: String = "Tiddles" override def toString =
s"$name is ${(if (dangerous) "dangerous" else " timid")}"
}
class Tiger(override val name: String) extends Cat { override val dangerous = true
}
object CatTest extends App { var c = new Cat()
println(c)
c = new Tiger("Tigger") println(c)
}

 

Protected Members

Protected Members

By default, within Scala all behavior (methods and functions), as well as data (properties), is public, that is they are visible (can be accessed) anywhere within an application.

 

We have seen that it is possible to mark both behavior and data as private so that they are only accessible within a single object or class. However, there is another option which has not been mentioned yet. That is, it is possible to make either behavior or data protected.

 

Protected members of a class are members (methods, functions, properties) that can only be accessed in the current class and in subclasses and only in subclasses. They are not visible to other elements of an application.

 

For example, in the following abstract class Base, the property age is public, the method max is public and the overridden method toString is public. However, the property working is only visible within Base and any subclasses of Base.

class Base(val name: String, var age: Int) { def max(x: Int, y: Int): Int = if (x > y) x else y val working = false

override def toString() = s"$name is $age"

}

The use of protected properties or behavior helps to explicitly specify the interface between a subtype and its supertype.

 

Restricting a Subclass

Restricting Subclass

You can restrict the ability of a subclass to change what it inherits from its superclass. Indeed, you can also stop subclasses being created from a class. This is done using the keyword final.

 

This keyword has different meanings depending on where it is used. For example in the following example, the keyword final has been applied to the whole class:

final class Employee(n: String,

a: Int,

company: String)

extends Person(n, a)

 

This means that no element of this class can be extended, so no subclass of Employee can be created. The keyword final can also be applied to public property. For example, final var maximum memory = 256

 

This indicates that the property maximum memory cannot be overridden in a subclass. This means that the value of maximum memory is set for this class and for all subclasses wherever they are defined by this class.

 

Using a value instead of a var means that the value cannot merely be overridden by a subclass, it is also only set once and is thus a constant for the hierarchy below the current class:

class Employee(n: String,
a: Int,
company: String)
extends Person(n, a) {
final val max = 10
}

The keyword final can also be applied to methods. This means that a method cannot be overridden in a subclass, for example,

class Volunteer (n: String ,
a: Int,
company: String)
extends Person(n, a) {
final def prettyPrint(): Unit = {
println("Volunteer")
println("\tName: " + name)
println("\tAge: " + age)
println("\tCompany: " + company)
}
}

This states that the method prettyPrint cannot be overridden in a subclass. That is, a subclass cannot redefine prettyPrint(); it must use the one that it inherits.

 

Restricting the ability to overwrite part of or all of, a class is a very useful feature. It is particularly important where the correct behavior of the class and its subclasses relies on the correct functioning of particular methods, or the appropriate value of a variable, etc.

 

A class is normally only specified as final when it does not make sense to create a subclass of it. These situations need to be analyzed carefully to ensure that no unexpected scenarios are likely to occur.

 

Abstract Classes

Abstract Classes

An abstract class is a class from which you cannot create an object. It is missing one or more elements required to create a fully functioning instance. In contrast, a non-abstract (or concrete) class leaves nothing undefined and can be used to create a working instance. You may wonder what use an abstract class is.

 

The answer is that you can group together elements that are to be shared amongst a number of classes, without providing a complete implementation.

 

In addition, you can force subclasses to provide specific methods ensuring that implementers of a subclass at least supply appropriately named methods. You should, therefore, use abstract classes when:

 

you wish to specify data or behavior common to a set of classes, but insufficient for a single instance, you wish to force subclasses to provide specific behavior.

 

In many cases, the two situations go together. Typically, the aspects of the class to be defined as abstract are specific to each class, while what has been implemented is common to all classes. For example, consider the following class.

abstract class Person(val name: String, var age: Int) { // Override inherited toString
override def toString = s"$name, $age";
//Define an abstract method
def prettyPrint
def birthday = age = age + 1
}

 

This is a revised version of the Person class we have seen several times before. However, we are now making Person an abstract concept. This means that you do not create instances o the Person class itself, but rather you create instances of subclasses of Person such as Employee, Student, Graduate, etc.

 

A person brings together the common features of these subclasses, but on its own, it is not sufficient to warrant an instance being created. It is only the concrete classes (non-abstract classes) which actually make sense as instances:

 

This abstract class definition means that you cannot create an instance of Person. Within the definition of Person, we can see that the toString and birthday methods are concrete or defined methods, whereas the method prettyPrint is not defined (it has no method body).

 

The prettyPrint method is what is known as an abstract method. Any class, which has one or more abstract methods, is necessarily abstract (and must, therefore, have the keywords, abstract class). However, a class can be abstracted without specifying any abstract methods.

 

An abstract class can also define any number of concrete methods. The method birthday is a concrete method that adds one to the current age of the person.

 

Any subclass of Person must implement the prettyPrint method if instances are to be created from it. Each subclass can define how to pretty print itself in a different manner. The following Graduate class provides a concrete class that builds on Person:

class Graduate(n: String, a: Int,
degree: String,
uni: String) extends Person(n, a) {
val institution: String = uni
def this(n: String, a: Int, degree: String) = this(n, a, degree, "Oxford")
override def toString =
s"Graduate ${super.toString} $degree ]";
def prettyPrint = {
println("Graduate")
println("\tName: " + name)
println("\tAge: " + age)
println("\tDegree: " + degree)
println("\tUniversity: " + uni)
}
}

This class extends the class Person and also provides:

The four-parameter constructor is used to passing the name and age for the Person class’s primary constructor and to provide a degree and University for the Graduate class.

 

A three-parameter auxiliary constructor invokes the four-parameter primary constructor.

A concrete version of the prettyPrint method.

 

We can also return to the Employee class from earlier and see that it also provides a concrete prettyPrint method and invokes the Person class’s primary constructor:

class Employee(n: String, a: Int, company: String)
extends Person(n, a) {
final def prettyPrint(): Unit = {
println("Employee")
println("\tName: " + name)
println("\tAge: " + age)
println("\tCompany: " + company)
}
}

 

The Super Keyword

Super Keyword

We have already seen that it is possible to override behavior defined in a parent class so that the version in the current class meets that needs of that class. The method toString is a typical example of this.

 

In numerous examples, we have redefined toString to create a string based on the data held by a class rather than to use the generic version.

 

To do this we used the keyword override and ensured that the method signature (its name, parameters and return matched those defined in the parent class).

 

However, rather than completely override the version of the method defined in the parent class we can choose to extend its behavior. This is done by defining a new version of a method in a subclass and then using the keyword super to invoke the version defined higher up the inheritance hierarchy.

 

For example, in the following example, the abstract class Base defines a method print that prints out a message “Base print”. The subclass Derived extends Base and overrides the method print.

 

However, within the body of the method, it called super. print that causes it to invoke the parent class’s version of the print. Note this call could be made anywhere within the body of the method print in Derived, it does not need to be the first line of the method.

abstract class Base {
def print = println("Base print")
}
class Derived extends Base {
override def print {
super.print
println("Derived print")
}
}

 

The effect of the overridden print method in Derived is that it calls the parent class’s version of the print. This means that in effect it extends, rather than replaces, the behavior of the original version of the print.

 

Note that super tells Scala to start searching up the class hierarchy for a version of print defined above the current class in the hierarchy.

 

In this case, it is defined in the parent class, but it could have been defined in a parent of Base—that is it starts searching Base and will continue search up the class hierarchy until it finds another definition of print to execute.

To illustrate this idea we could create a simple application:

object Test extends App {

var d = new Derived()

d.print }

 

Scala Type Hierarchy

The type hierarchy in Scala is complicated by the presence of traits, but the core types are divided into two types, AnyVal, and AnyRef, with the class Any at the root.

 

Thus the root of everything in Scala is the abstract class Any. Any has two subclasses, the abstract AnyVal, and the concrete AnyRef:

 

AnyVal this is used to represent Value like types, such as Boolean, Char, Byte, Short, Int, Long, Float, Double. Strictly speaking, Scala has no primitive types— these are objects. However, they are a special type of objects that are managed by the Scala runtime efficiently.

 

AnyRef this is used for all reference types such as classes and traits. Examples of AnyRef subtypes include the data structure (or collection) classes such as Array, List, Seq and String. It is also used as the root for all user-defined classes that do not explicitly extend any other class.

 

Polymorphism

Polymorphism was a concept discussed earlier in the blog relating to one of the four key concepts in Object Orientation. In terms of Scala, programming Polymorphism means that a value or var local variable or property can actually reference an instance of a particular type or any subtype of that type.

 

Thus a var of type Person can actually hold a reference to a Person (assuming it is not an abstract type) or any subclass of Person (including Student, Employee, and graduate, etc.).

 

For example, we can write:

object TestPolymorphism extends App {
var p: Person = new Graduate("Bill", 21, "English")
println(p)
println("----------------------------")
p.prettyPrint
println("----------------------------")
p = new Employee("Adam", 32, "MyCo")
println(p)
println("----------------------------")
p.prettyPrint
println("----------------------------")
}

 

In this test application, the variable p is of type Person. It can thus reference a Person, a Graduate or an Employee. Initially, we are storing a reference to a Graduate in p. We then call println on p (which causes the toString method to be invoked on the instance reference by p and then call prettyPrint on p.

 

Both toString and prettyPrint can be guaranteed to be available in whatever instance prefers to because the functionality in the class Person guarantees it. Any methods defined only in the Graduate are not visible via p (although they are still present in the instance being referenced, they just cannot be accessed at this point).

 

After this, we create a new instance of the Employee class and store a reference to that instance in p and then use toString to print the object out and prettyPrint again. However, the behavior that now executes is whatever behavior is either defined in Employee or inherited into Employee.

 

Control Structures

The if Statement

The basic format of an if statement in Scala is the same as that in languages such as C, Pascal, and Java. A test is performed and, depending on the result of the test, a statement is performed. A set of statements to be executed can be grouped together in curly brackets { }. For example,

if (a == 5)
println("true")
else
println("false")
if (a == 5) {
print("a = 5")
println("The answer is therefore true")
} else { print("a != 5")
println("The answer is therefore false")
}
Of course, the if statement need not include the optional else construct:
if (a == 5) {
print("a = 5")
println("The answer is therefore true")
}
You must have a Boolean in a condition expression, so you cannot make the same equality mistake as in C. The following code always generates a compile time error:
if (a = 1) {
…
}
Unfortunately, assigning a Boolean to a variable results in a boolean (all expressions return a result) and thus the following code is legal, but does not result in the intended behaviour (the string “Hello” is always printed on the console):
var a = false
if (a = true)
println("Hello")
You can construct nested if statements, as in any other language:
if (count < 100)
if (index < 10)
{…}
else
{…}
else
{…}
However, it is easy to get confused. Scala does not provide an explicit if-then-elseif-else type of structure. In some languages, you can write:
if (n < 10)
print ("less than 10");
else if (n < 100)
print ("greater than 10 but less than 100");
else if (n < 1000)
print ("greater than 100 but less then 1000");
else
print ("greater than 1000");

 

This code is intended to be read as laid out above. However, if we write it in Scala, it should be laid out as below:

if (n < 10)
print ("less than 10")
else if (n < 100)
print ("greater than 10 but less than 100")
else if (n < 1000)
print ("> than 100 but < 1000")
else
print ("> than 1000")

This code clearly has a very different meaning (although it may have the same effect). This can lead to the infamous “dangling else” problem. Another solution is the switch statement. However, as you will see the switch statement has significant limitations.

 

If Returns a Value

Almost all statements in Scala return a result, and the if statement is no different. This means that you can use an if statement to determine the value to assign to a value (or pass to a method, etc.).

 

For example, the following code assigns either the string “Dad” or the string “No Data” to the value role defining the current string referenced by the variable name:

val role =
if (name == "John")
"Dad"
else
"No Data"
println(role)

This is a very useful feature of the if statement which can be used effectively in many situations.

 

[Note: You can free download the complete Office 365 and Office 2019 com setup Guide.]

 

Iteration

Iteration in Scala is accomplished using the for, while and do-while statements. Just like their counterparts in other languages, these statements repeat a sequence of instructions a given number of times.

 

For Loops

A for loop in Scala is very similar to a for loop in other languages. It is used to step a variable through a series of values until a given test is met. Many languages have a very simple for loop, for example,

for i = 0 to 10 do
…
endfor;
In this case a variable I would take the values 0, 1, 2, 3, etc., up to 10. The longhand form of this in Scala is:
for (i <- (0).to(10)) {
print(i)
}

 

Note that in the above to is a method calls on the Int (integer) type. In practice, this is a lower level implementation issue, and it would be far more common to write:

for (i <- 0 to 10) print(i)

This can be done as Scala can infer the brackets and the dot which has the benefit that it will look far more familiar as a language construct to those used to programming languages such as C and Pascal.

 

One thing to note is that the operator here includes the value 10, whereas in languages such as C and Java it would mean up to but not including 10.

 

Multiple indexes can be used with a for a loop. For example, we could increment I from 1 to 3 and j from 5 to 7:

object MultipleForLoopTest extends App { for (i <- 1 to 3; j <- 5 to 7) {
print("Value of i: " + i);
println(" / Value of j: " + j);
}
}
This may not have the effect you expect. This equates to loop the value of I through 1 to 3 for each of the values of j; thus, the output is:
Value of i: 1 / Value of j: 5
Value of i: 1 / Value of j: 6
Value of i: 1 / Value of j: 7
Value of i: 2 / Value of j: 5
Value of i: 2 / Value of j: 6
Value of i: 2 / Value of j: 7
Value of i: 3 / Value of j: 5
Value of i: 3 / Value of j: 6
Value of i: 3 / Value of j: 7

As you can see from this, the value of I remains constant for all values of j and is then incremented for a repeat for the values of j.

 

Until

An alternative to the to the operator is the until operator which indicates that a variable I should loop unto but not including the higher bound, thus:

for (i <- 1 until 4) println(i)
Producers the output:
1
2
3
But does not include the value 4.

 

For Loop with a Filter

Another option with the for loop is to include a filter into the looping process. This can be used to filter out those elements within a loop that you do not want to process.

 

A filter is an additional logical test added to the for loop following the iteration values already presented. For example, assuming that the variable files contain some form of a list of files, we can add an extra test to check so that we only print out files where the filename ends with “.txt”:

 

for (f <- files if f.getName.endsWith(".txt"))

println(f)

 

With this loop each file in the list of files is tested such that the name is first obtained (getName) and then the string method ends with tests to see if the filename ends with “.txt”; if it does, then the file is processed by the loop—which in this case involving printing out the file.

 

If the filename does not end with “.txt,” then the loop immediately moves onto the name file in the list.

 

The complete program for this example is shown below:

import Buy & Sell Domain Names.File
object FileLoopTest extends App {
val files = (new File(".")).listFiles
for (f <- files if f.getName.endsWith(".txt"))
println(f)
}

 

Note that any number of if conditions can be added to provide multiple filters on a for a loop. Each if the condition is separated by a ‘;’, for example,

for (f <- files if f.getName.startsWith("Help"); if f.getName.endsWith(".txt"))

println(f)

Longhand for Loop

Although we have looked at a number of different for loops in the preceding sections, they are all subsets of the full for loop which is made up of a generator, an optional definition, and a filter. Thus you could write:

for (
p <- persons ; // a generator
n = http://p.name ; // a definition
if (n startsWith "To") // a filter
) println(n)
With the definition being reset each time round a loop.

 

For-Yield Loop

A special for loop is a for-yield loop. It is particularly useful for collecting together a set of results from a for loop that can be processed by other code rather than performing the processing directly within the for a loop.

 

That is, the value of the all the previous for loops (from the point of view of the expression being evaluated when the for loop executes) is Unit or nothing.

 

However, using a yield then each time around the loop a yield expression can be evaluated and the results of the expression are collected together and made available to subsequent lines of code once the for loop has completed.

 

The general syntax for a for-yield loop is:

for (sequence) yield expression

Examples of the use of the for-yield loop are shown below:

val data = for (i <- 1 to 5) yield 10 * i

produces a sequence of values (10, 20, 30, 40, 50) held in the val data

val info = for (n <- List("one", "two", "three")) yield n.substring(0, 2)

produces a list of values List(on, tw, th) held in info.

In both cases, the subsequent code could process either the data variable or the info variable. This is a very powerful construct for creating a collection of data items to be further processed from some loop-based operations.

 

While Loops

The while loop exists in almost all programming languages. In most cases, it has a basic form such as:

while (test expression)

statement

This is also true for Scala. The while expression controls the execution of one or more statements. If more than one statement is to be executed, then the statements must be enclosed in curly brackets { }:

var i=0;
while (i < 10) {
println(i)
i += 1
}

The above loop tests to see if the value of i is less than or equal to 10, and then prints the current value of i before incrementing it by one. This is repeated until the test expression returns false (i.e. i = 11). 

 

You must assign I an initial value before the condition expression. If you do not provide an initial value for I, it defaults to none value, and the comparison with a numeric value raises an exception.

 

Do Loops

In some cases, we want to execute the body of statements at least once; you can accomplish this with the do loop construct:

  • do
  • statement
  • while (test expression);

This loop is guaranteed to execute at least once, as the test is only performed after the statement has been evaluated. As with the while loop, the do loop repeats until the condition is false.

 

You can repeat more than one statement by bracketing a series of statements into a block using curly brackets { }:

 style="margin:0;width:972px;height:95px">var n = 10
do {
println(n)
n= n – 1
} while (n > 0)
The above do loop prints the numbers from 10 down to 1 and terminates when n = 0.

 

An Example of Loops

As a concrete example of the for and while loops, consider the following class. It possesses a method that prints numbers from 0 to the MaxValue variable:

class Counter {
var MaxValue = 10
def count() = {
var i = 0
println("----- For -------------");
for (i <- 0 to MaxValue) {
print(" " + i)
}
println(" ")
("----- While -----------")
i = 0
while (i <= MaxValue) {
print(" " + i)
i = i + 1
}
println(" ")
println("-----------------------")
}
}
object Counter extends App {
val c = new Counter()
c.count
}
The result of running this application will be:
----------For--------------
0 1 2 3 4 5 6 7 8 9 10
----------While------------
0 1 2 3 4 5 6 7 8 9 10
-----------------------------

 

Equality

Two instances may be considered equivalent if their contents are the same. This equivalence is defined by the equals method on a class and is used by the ‘==’ and ‘!=” operators, where: == tests for equality and ! = tests for not equals

 

The equality operators are actually invoked on the left-hand operand with the right-hand operand being passed to the operator as a parameter.

You can compare two instances using == and ! = , for example,
1 == 2 // false
1 ! = 2 // true
1 == 1.0 // true
List(1, 2, 3) == List(1, 2, 3) // true
List(1, 2, 3) == ”John” // false

Note that there is also a referential equality operator in Scala. This is provided by ‘eq’ method. This method tests that the two instances being compared are literally the same instance rather than the just equivalent in value.

 

Recursion

Recursion is a very powerful programming idiom found in many languages. Scala is no exception. The following class illustrates how to use recursion to generate the factorial of a number:

class Factorial {
def factorial(number: Int): Int = {
println(number)
if (number == 1)
return 1
else
return { number + factorial(number - 1) }
}
}
object FactorialTest extends App {
val f = new Factorial()
println("= " + f.factorial(5))
}

One problem for recursion is that although it is elegant to program, it may not be the most efficient runtime solution. However, Scala can optimize tail-recursive methods such that it can be expressed via recursion in terms of the program but optimized into an iterative loop at runtime.

 

Since Scala 2.8, you can now mark a method that you expect to use tail recursion with the annotation @tailrec. An annotation is a piece of metadata that the runtime can use to perform additional processing, etc.

 

This allows you to indicate that the method should be optimizable for tail recursion by the compiler. It thus allows the compiler to provide a warning if the method does not succeed in being tail recursive. For example,

package com.jjh.Scala.tail
import Scala.annotation.tailrec
object Util {
def factorial(n: Int): Int = {
@tailrec
def factorialAcc(acc: Int, n: Int): Int = {
if (n <= 1) acc
else factorialAcc(n * acc, n - 1)
}
factorialAcc(1, n)
}
}

To understand why this makes a difference consider the following method:

The method bang intentionally throws an Exception (causes an error to occur) when x is Zero. This allows you to see what the compiler has done with the runtime version of the code.

 

With the +1 element of the else part of the if statement commented out this is a tail-recursive method. When we run this program, the output is as shown below:

 

If we now uncomment the +1 at the end of the ‘if’ statement and rerun this program, we now get:

What you can see is that the first version has been converted into an iterative loop which does not need to keep calling itself (which is inefficient at runtime).

 

However, with the second example, we have called the same bang method multiple times which has resulted in the need to handle each call separated (set-up the call stack for each method invocation) which is far less efficient.

 

Thus knowing whether the recursive method is tail recursive or not is an important consideration.

 

The Match Expression

Scala’s match expression allows for a selection to be made between a number of alternative tests and as such is similar in nature to the case statement in Pascal and C or the switch statement in Java.

 

However, compared to the switch statement in Java allows much wider pattern matching capability in the case clause of the expression this provides for a far more powerful and flexible construct.

 

Also, note that the match expression is an expression (and not just a statement), thus it returns a value and can be used as part of an assignment clause.

 

The pattern element of the match expression is much more flexible than in languages such as C and Java and can be any one of the following:

  • a literal,
  • a wildcard (to match anything),
  • a type,
  • a variable which will be populated,
  • of different types,
  • tuple patterns, etc.

As an example, consider the following simple literal match test:

object MatchTest extends App {
val arg = "John"
val relationship =
arg match {
case "John" => "Dad"
case "Denise" => "Mum"
case "Phoebe" => "Daughter"
case "Adam" => "Son"
// Default / wildcard
case _ => "WhoAreYou?"
}
println(relationship)
}

This example compares the arg varl with the String literals “John”, “Denise”, “Phoebe” and “Adam”.

If it is one of these values, it returns the string associated with that literal.

Thus if arg holds the string “John”, then the match expression will return the String “Dad”.

The result of the match expression is then saved into the relationship val and printed out.

 

If the value held in arg is not one of the strings explicitly mentioned then it will use the default (wildcard) case test. Thus any other string in arg will result in “WhoAreYou?” being returned by the match expression. Note in match expression “_” is being used as a wildcard that will match any string not mentioned above in the case tests.

 

However, unlike many other languages, the literals used in the individual case tests do not need to be of the same type. For example, the following uses four different types of literal from an Int to a Boolean, to a string and an empty list (Nil):

object MatchTest2 {
def main(args: Array[String]): Unit = {
println(describe(5))
println(describe(true))
println(describe("hello"))
println(describe(Nil))
println(describe(List(1, 2, 3)))
}
def describe(x: Any) = x match {
case 5 => "five"
case true => "truth"
case "hello" => "welcome message"
case Nil => "The empty list"
case _ => "something else"
}
}

 

The type of the parameter x is Any which is the root of everything in Scala, and thus x can indeed hold any type of value. We can now use the method described to produce a printed string for any type of value in Scala—although unless it is the value 5, true, “hello” or Nil, it will print the description as “something else”.

 

A variable on the last example allows us to use a variable in the case test of the wild card to obtain the actual value passed in and to use that in the expression being evaluated:

object MatchTest3 {
def main(args: Array[String]): Unit = {
println(describe(5))
println(describe(true))
println(describe("hello"))
println(describe(Nil))
println(describe(List(1, 2, 3)))
}
def describe(x: Any) = x match {
case 5 => "five"
case true => "truth"
case "hello" => "welcome message"
case Nil => "The empty list"
case somethingElseVariable => "something else: " + somethingElseVariable
}
}

 

Now when something other than the value 5, true, “hello” or Nil is passed in, then the result will be that the string “something else: “concatenated with that thing in string format will be returned by the described method.

 

We can also match by type rather than specified value. For example, in the following example if the type of data passed in is a String, then the first case test will pass and the string will be bound to the variables and thus available for processing in the resultant operation associated with that test.

 

In turn, if the instance passed into getSize is a List, then it will pass the second test and be bound to the variable l and be available to the operation following this test. If the instance passed to getSize is actually a Map, then it will meet the criteria specified by the third case test.

 

The result in each case is that an appropriate method is called to determine the size of the instance passed to getSize. If the instance is not a String, a List or a Map, then −1 is returned by default:

object MatchTest4 {
def main(args: Array[String]): Unit = {
val name = "John"
println(getSize(name))
val xxx = List(1, 2, 3)
println(getSize(xxx))
val myMap = Map("London" -> "01", "Cardiff" -> "020")
println(getSize(myMap))
val otherMap = Map(1 -> "a", 2 -> "b", 3->"c")
println(getSize(otherMap))
val lectureMap =
Map("John" -> "Scala", "Fed" -> "PHP")
println(getSize(lectureMap))
val info = (1, 2, 3)
println(getSize(info))
}
def getSize(x: Any) = x match {
case s: String => s.length()
case l: List[_] => l.size
case m: Map[_,_] => m.size
case _ => -1
}
}

Scala & Java Interoperability

In this blog, we will look at the interoperation of Java and Scala. Both Java and Scala are JVM bytecode languages.

 

That is, they both compile to the bytecode language that is understood by the JVM. The bytecode language of the JVM was originally designed to be the compiled form of Java and was what the Java Virtual Machine executed.

 

However, things have evolved such that today the JVM is a virtual environment for executing bytecode languages.

 

In fact, there are now several languages that can be compiled to JVM bytecodes including Java, Groovy, Clojure, JRuby, Jython, JavaScript, Ada, Pascal as well as Scala. A common term used for these languages is that they are all bytecode languages.

 

As such at the bytecode level, there is no difference between Java and Scala— they are just different starting points for the same destination. Therefore, at runtime it is only the bytecode that executes—there is no such thing as Java or Scala. Scala can thus interoperate with other bytecode languages.

 

A Simple Example

As a simple example, consider the Scala Person shown below:

package com.jjh.java

class Person (name: String="John", var age:Int=47)

 

This class compiles to a Person. the class file just as any other bytecode language. This means that it can be used within Scala or Java. The following code sample illustrates the use of the Scala class Person within a Java application:

package com.jjh.java;
/**
This is a standard Java class with a main method.
But note that Person is a Scala class.
*
This illustrates the interop between Scala and
Java.
*/
public class JavaInteropTest1 {
public static void main(String [] args) {
System.out.println("Hello from Java");
Person p = new Person("Granny", 85);
System.out.println(p);
}
}

Notice that as far as Java is concerned that this is a class Person with a constructor that takes a String and an Integer. Also, notice that both the Scala class Person and the Java class JavaInteropTest1 are in the same package.

 

This works because from a bytecode point of view, there is no difference between a Scala class Person in the package com.jjh.Scala and a Java class Person in the same package—they are both byte classes in the package com.jjh. Scala.

 

The output from this application is given below in IntelliJ IDEA:

One thing to note is that the Scala class Person defines some slightly strange (from a Java perspective) methods for the property age. It defines the age() accessor method (not getAge) and the age_$eq method for setting the new value of age (as it’s a var):

System.out.println(p.age());
p.age_$eq(55);
System.out.println(p.age());
The output of these lines is:
85
55

 

Inheritance

It is possible to inherit between Java and Scala classes. For example, in the following, Employee is a Java class while Person is the Scala class presented in the previous section. The rules for constructors are maintained, and the Java class can call the superclass constructor.

package com.jjh.java;
/**
This is a Java class that extends a Scala class!
Note the call to super for the parent class constructor.
*/
public class Employee extends Person {
private String company;
public Employee(String name, int age, String company) { super(name, age);
this.company = company;
}
public String toString() {
return super.toString() + ", " + company;
}
}

 

Issues

There are of course some issues related to interoperating between Scala and Java. The first one of which is that Scala requires a Java 8 or newer runtime to operate within.

 

The other is that Scala has some concepts that Java has no knowledge of such as objects, traits, and functions as first-class language elements. This list may change with the future versions of Java. In turn, Scala has no concept of an Interface. Therefore, if care is not taken, problems may arise.

 

In general, Scala to Java interoperability is relatively seamless as Scala builds on top of Java. However, Java to Scala can sometimes be problematic. The most common set of issues are:

  • Java has no equivalent of Traits.
  • Functions are object values.
  • The Scala-type system is more complex.
  • Scala has no notion of static, so can’t access statics in the same way as Java code.
  • Java doesn’t understand Scala’s companion module.
  • Java sees Scala objects as a final class with statics.

 

Scala Objects

Of course, the underlying bytecode representation also lacks many of the features of Scala presented above. Scala therefore often generates more than one-byte code class or type for a single Scala concept. This is illustrated below for a simple test Scala object. The following code generates two .class files:

package com.jjh.java
object Test {
val name = "MyName"
def print = println(s"Print $name")
val func = () => println("Printer function $name")
}

The two .class files are Test.class and Test$.class:

 

This can make it difficult to decide how to treat a Scala concept from the Java side. It can be useful to examine what these class files contain in order to understand the Java view of these structures.

 

This can be done using the Java_p program. Java_p is the class file disassembler distributed with the standard Oracle SDK. The result of using the Java_p program with the Test examples is shown below:

 

What this shows is that the Scala object Test is represented at the bytecode level by a public final class Test and a second public final class Test$.

 

How would you then call the Scala object Test from java? From the java decompilation, you have a choice of the Test class with a static method print and the Test$ class with a non-static method print.

 

In fact, in this case from Java, we can just treat the object Test as if it was a statically defined entity. This is because the static method on a final class means that we cannot extend the class Test, and then we can call the method without needing to instantiate the class. Thus, we can just write:

package com.jjh.test;
import com.jjh.java.Test;
public class JavaTest {
public static void main(String[] args) {
Test.print();
}
}

Which is semantically the closest we can get in Java to the concept embodied in the Scala Object.

 

Companion Modules

Companion modules are another point of conflict in that Scala has the concept of a class and an associated companion object (which must have the same name as the class and be defined within the same file). For example,

package com.jjh.companion
/**
The Companion class
*/
class Session(var id: Int) {
}
/**
Its Companion (singleton) object
*/
object Session {
private var counter = 0
def session() = {
counter = counter + 1
new Session(counter)
}
}

This listing when compiles generates the classes shown below:

If we use the Java_p de-compiler again on these classes, then we can see that the bytecode Session class combines elements of both the Scala Session class and the Scala Session object.

 

However, note that the Session is not final and thus can be extended! This is shown below where the Java_p program is used to de-compile both the Session and the Session$ classes.

 

If you need to access the Session from within Java code, then this can be done as the Session just looks like a normal Java class with a static factory method:

package com.jjh.companion;
public class JavaTest {
public static void main(String[] args) {
Session session = Session.session();
System.out.println(http://session.id());
}
}
Indeed you can even be able to extend it. For example,
package com.jjh.companion;
public class ExtendedSession extends Session{
public ExtendedSession(int id) {
super(id);
}
}

 

Traits

Scala has Traits—these are a type within the Scala-type system, and they are neither abstract classes nor are they Java interfaces. Java does not have Traits although it does have interfaces and abstract classes. There cannot, therefore, be a direct mapping from a Scala trait to a Java concept.

 

However, this is also true of the underlying bytecode representation—it does not have a concept of a Trait. This raises the question of what happens when a trait is defined at the bytecode level? For example, given the trait Model is shown below, how is this represented at the bytecode level:

package com.jjh.traits

trait Model {

def info(x: String):String

}

If you examine the .class files generated for this type, you will see that the single trait Model is represented by a single .class file Model.class.

 

 The file Model.class defines an interface containing a single public abstract method that takes a string and returns a string. This can be implemented by Java class who wish to implement the Trait.

 

However, what happens if the trait defines actual behavior and data. This is the biggest area of change in terms of Java interoperability from pre-Scala 2.12 to post-Scala 2.12. Prior to Scala 2.12, a trait was represented as an interface and a class that held any concrete implementations defined in the trait.

 

In Java 7 and older versions of Java, interfaces were only allowed to define method signatures (abstract methods) and static final constant values. The following listing defines a modified version of the Model trait that contains behavior (the print method) and data (the title variable):

package com.jjh.traits
trait Model {
var title = "CS123-10"
def info(x: String):String
def print = println("Hello World")
}

This is used to result in two class files being generated as shown in here:

 

In this older version of Scala, the Model. the class file contained an interface definition Model with a single public abstract method info.

 

The Model$class.class file contained an abstract class called Model$class that extends the java.lang.Object type and defined two static methods one info and the other $init$ both of which take a Model as their parameters.

 

This made interoperability of such traits with Java difficult!

However, Java 8 introduced new features to interfaces that allow interfaces to have concrete methods defined. This means that Scala 2.12 (and newer) is able to compile a trait to a single interface class file.

 

This means that from the Java world, a Trait appears as an interface to implement.

However, there are still a couple of things to bear in mind. The first is that the interface that has been generated has three abstract methods that must be provided by any Java class that wish to implement this Interface.

 

The first of these is fairly obvious; the method info(x: String) is an abstract method in the Scala trait is, therefore, an abstract method in the Java interface.

 

However, the other two methods are

String title() and

void title_eq(String)

 

Neither of these methods I listed in the Scala trait—instead there is a var property called title. What has happened here is that the Scala compiler converts a value or a var into 1 or two methods (one for reading and one for writing) the value.

 

As this is a var, there are two methods required. However, no default implementation is given and thus the subclass must provide appropriate implementations.

 

In this case, however, the var is initialized to the string “CS123-10”. It is therefore also necessary to provide a way to initialize such properties. This is provided by the slightly strangely named method:

public static void $init$(com.jjh.traits.Model)

 

This method can be invoked in the subclass (e.g. in the constructor) to initialize any properties defined in the Trait. This is done via reference to the interface name:

Model.$init$(this);

 

The end result is that the class implementing the Model interface in Java looks like this:

package com.jjh.traits;
public class Foo implements Model {
private String _title;
public Foo() {
Model.$init$(this);
}
public String info(String x) {
return "title: " + this.title() + " with " + x;
}
public String title() {
return this._title;
}
public void title_$eq(String s) {
System.out.println("Setting title: " + s);
this._title = s;
}
}

 

As this example illustrates, some further work is still involved, so care must be taken if a trait is meant to be implemented in Java. Briefly, if a trait does any of the following, its subclasses require synthetic code:

 

defining fields (Val or var, but a constant is ok—final value without result type) calling super initializer statements in the body extending a class relying on linearisation to find implementations in the right super trait Scala is a hybrid Object-Oriented and functional language.

 

However, Java (at least up until Java 8) is an Object-Oriented language and thus has no concept of a Function.

 

Scala, of course, treats Functions as top-level entities or first-class elements in the language with functions making up part of the type system of the language.

 

However, the underlying bytecode representation to which Scala compiles also does not have a concept of a function thus there must be some form of mapping from the Scala world into the bytecode world. This thus means that from the Java side of things that mapping can be exploited.

 

Functions are actually represented at the bytecode level by the various Function types that model a function. The following code which defines an object called MyScalaTest containing a method which takes a single parameter of type Int => String.

 

This method is represented at the bytecode level as being a method that takes a single parameter of type Function1 which is parameterized to use an Object and a String as the types involved.

>package com.jjh.func
object MyScalaTest {
def setFunc(func: Int => String) {
println(func(10))
}
}

The object MyScalaTest is represented by two .class files at the bytecode level as illustrated below.

 

This is shown when we use Java_p to de-compile the compiled version of the MyScalaTest.

The MyScalaTest.class contains a final class that defines a single public static method setFunc that takes a parameter of type Scala.Function1.

The associated MyScalaTest$ class defines a non-static (instance side) method setFunc that also takes a Scala.Function1 parameter.

 

In fact, within Scala, there are a range of types (actually Traits) that are used to represent functions from Function1, Function2 and Function3 through to Function22. Therefore functions can have up to 22 parameters.

 

This appears to be an arbitrary choice and is limited only because the underlying types are only written from Function1 through to Function22. If you find yourself hitting this limit, then you probably need to rethink your design!

 

As an example, if we changed the function type taken by setFunc to have three input parameters and one result, as follows:

package com.jjh.func
object MyScalaTest {
def setFunc(func: (Int, Int, Int) => String) {
println(func(10, 1, 2))
}
}

 

Then when we de-compile the resulting .class bytecodes, we would find that this is now as shown below.

 

To exploit this within the Java world, we can create Java code that implements the Function1 type (which appears as an Interface in the Java world). This can be done by using one of the abstract runtime classes that implement the appropriate interface.

 

For example, if we have a single parameter function, then the interface for it in the Java world is Function1 and the abstract class that provides the basic infrastructure for that interface is Abstract Function1 (whereas for a three-parameter function we would use the Function3 interface and the AbstractFunction1 class).

 

The following listing illustrates how we can create a Scala function in Java and use it with a Scala object that expects to receive a function:

package com.jjh.func;
import Scala.Function1;
import Scala.runtime.AbstractFunction1;
public class FuncTest {
public static void main(String[] args) {
Function1<Object, String> f =
new AbstractFunction1<Object, String>() {
public String apply(Object someInt) {
return "Hello world: " + someInt;
}
};
MyScalaTest.setFunc(f);
}
}

In the above example, a new (anonymous) inner class is created on the fly based on the AbstractFunction1 class. It defines a method apply that takes a parameter of type Object (anything in the Java world) and returns a String. In this case, the string is constructed by prefixing whatever was passed in with the string “Hello World”.

 

Using this new anonymous class, a new instance is created and a reference to that instance stored in the variable ‘f’ which is of type Function1. Note that the parameters to Function1 indicate that the function being defined takes one parameter (the first type in the angle brackets ‘<>’ and the return type is String the second type in the angle brackets.

 

The instance referenced by f is then passed on the setFunc method of MyScalaTest, which is the Java representation of the MyScala Test object. The end result of executing this Java program is the output:

 

It is worth noting that Scala does not use Java’s representation of functions. This is primarily for two reasons, firstly Scala had functions before Java did and thus developed its own representation but secondly (and more importantly) Scala’s representation of functions is considerably richer than that in Java 8.

 

Collection Classes

Both Java and Scala have libraries of collection classes, and confusingly many of the names are similar.

 

As an example, there is a Set collection in both Java and Scala and there is a List in both Java and Scala. However, the Scala collections are not just wrappers around the Java collection classes and thus you cannot just assign from one to the other.

 

For example, you cannot assign a Java Set type to a Scala Set type. The following does not work:

package com.jeh.Scala.interop
object SetTest1 extends App {
val jSet: java.util.Set[String] = new
java.util.HashSet[String]()
jSet.add("Adam")
jSet.add("Phoebe")
val sSet: Scala.collection.mutable.Set[String] = jSet
}

If you do try this, you will find that the assignment of the jSet to the Scala Set will result in a compilation error. There is no relationship between these two types of Sets to allow a cast to occur.

 

You might think that you can create a new instance of the Scala set using the Java

Set as a parameter to the constructor: val sSet: Set[String] = Set(jSet.toArray(): _*)

 

For an appropriate Scala set, this could work but the array type generated by Java is an Array of Objects, but we are using a Set with an array of String (which is what both types of sets hold).

 

It is, therefore, necessary to look at the Java Converter facilities provided in the Scala collections package. For example, the JavaConverters object provides numerous conversions.

 

The following conversions are supported via asJava, asScala

Scala.collection.Iterable <=> java.lang.Iterable
Scala.collection.Iterator <=> java.util.Iterator
Scala.collection.mutable.Buffer <=> java.util.List
Scala.collection.mutable.Set <=> java.util.Set
Scala.collection.mutable.Map <=> java.util.Map
Scala.collection.mutable.ConcurrentMap <=> java.util.concurrent. ConcurrentMap

 

The following conversions are supported via asScala and through specially named extension methods to convert to Java collections, as shown:

Scala.collection.Iterable <=> java.util.Collection (via asJavaCollection)
Scala.collection.Iterator <=> java.util.Enumeration (via asJavaEnumeration)
Scala.collection.mutable.Map <=> java.util.Dictionary (via asJavaDictionary)
In addition, the following one-way conversions are provided via asJava:
Scala.collection.Seq => java.util.List
Scala.collection.mutable.Seq => java.util.List
Scala.collection.Set => java.util.Set
Scala.collection.Map => java.util.Map

 

The following one-way conversion is provided via asScala:

java.util.Properties => Scala.collection.mutable.Map Thus, the earlier example should be written as follows.
package com.jeh.Scala.interop
import Scala.collection.JavaConverters._
object SetTest1 extends App {
val jSet: java.util.Set[String] = new java.util.HashSet[String]()
jSet.add("Adam")
jSet.add("Phoebe")
val sSet: Scala.collection.mutable.Set[String] = jSet.asScala
}

However, note that the asScala returns a mutable set, not an immutable set. This is because Java collections are all mutable whereas Scala has mutable and immutable collections.

 

It should also be noted that the JavaConverters classes use the Adapter pattern to wrap the original Java collection (the underlier) within a Scala interface that resembles the Scala collection types.

 

Thus, both converting and accessing converted collections is a constant time (O(1)) operation introducing only a minor overhead. Due to this Design Pattern, it is also worth noting that converting Java collection to Scala and then back to Java yields the original collection, not double-wrapper.

 

Implementing a Java Interface

Java makes extensive use of interfaces, which are used to define abstract definitions of method signatures (and static constant values). Scala has no concept of an interface; however; it does have traits.

 

One of viewing an interface is as a very restricted type of trait. Thus, a Scala class can implement a Java interface by treating it as a Trait which it is mixing into that class.

 

However, unlike Scala traits, interfaces can only have abstract methods. Thus, the class mixing in the interface must implement the method (or methods) specified by the interface.

For example, given the following Java interface:

package com.jjh.java;

import java.util.List;

public interface Processor {

double calc(List<String> l);

}

 

Any Scala class must provide an implementation for the abstract method calc. Note also that the method call takes a List, which in the Java world is an interface itself. Thus whatever is passed into the calc method will be a class or an object that implements that interface.

 

The following listing provides a simple Scala class that implements the Processor interface.

package com.jjh.interop
import java.util.List
import com.jjh.java.Processor
class MyProcessor extends Processor {
def calc(l: List[String]): Double = {
import Scala.collection.JavaConverters._
val list = l.asScala
return list.foldLeft(0)
{ (total, element) => total + element.toInt }
}
}

 

The simple class MyProcessor converts the Java list into a Scala list to make it easier to work with. It then processes all the elements within the list in order to generate a total (it is assumed that all the string in the list passed in will contain integer values allowing the to Int operation to convert the string into an integer).

 

The result returned is a Double (which is treated as a raw value double in the Java world).

 

The interesting thing is that the Scala class MyProcessor could be used from the Scala world or from the Java world. Thus the following listing implemented in Java creates a new instance of the MyProcessor and stores it into a variable of type Processor. It then creates a list of strings and passes them into the MyProcessor object, etc.

package com.jjh.java;
import java.util.ArrayList;
import java.util.List;
import com.jjh.interop.MyProcessor;
public class TestApp {
public static void main(String[] args) {
Processor proc = new MyProcessor();
List<String> l = new ArrayList<String>();
l.add("32");
l.add("5");
double x = proc.calc(l);
System.out.println(x);
}
}
The output of this program is 37.0.
 

Recommend