For
all the work in this post, we will use Read-Evaluate-Print-Loop
Scala interpreter. Some familiarity
with Java will be of great help in understanding Scala. In this post, we will
look at Operator Overloading.
All operators like + , - , * , / , % in Scala mean the same as in other languages as Java and follow the infix notation. Infix notation means that the operator in placed in between the operands. For example, for multiplying two integers integer_1 and integer_2, we write:
integer_1 * integer_2
The other two notations are prefix notation and postfix notation. In prefix notation, the operator is placed before the operands, and in postfix notation, the operator is placed after the operands.
In Scala, all operators are methods. So, a multiplication operation between two integers can be written as:
3 * 5
or
3.*(5)
The results are shown below:
scala> 3 * 5
res42: Int = 15
scala> 3 . * (5)
res43: Int = 15
Since operators are methods in Scala, we can create a method, say, with name, * , to mean something different but imply the same product spirit in a new context. Let us see the program for multiplication of complex numbers. It is not straightforward as any complex number has a real part and an imaginary part. The formula for the product of complex numbers is shown below:
(a + bi) * (c + di) = (ac-bd) + (ad + bc)i ---------------------------- (1)
This can be implemented in Scala by first defining a complex number class that takes two parameters, one for the real part and another for the imaginary part. Then, we can define a method with the symbol * that will return the product of the multiplication operation between the two complex numbers.
The class with the real and imaginary parameters is defined below:
class ComplexNumber(val real_part : Double, val imaginary_part : Double)
We add the multiplication method as shown below:
class ComplexNumber(val real_part : Double, val imaginary_part : Double) {
def *(Second_Operand: ComplexNumber) = {
new ComplexNumber(this.real_part*Second_Operand.real_part - this.imaginary_part*Second_Operand.imaginary_part, this.real_part*Second_Operand.imaginary_part + Second_Operand.real_part*this.imaginary_part)
}
def multiply(Second_Operand: ComplexNumber) = {
new ComplexNumber(this.real_part*Second_Operand.real_part - this.imaginary_part*Second_Operand.imaginary_part, this.real_part*Second_Operand.imaginary_part + Second_Operand.real_part*this.imaginary_part)
}
override def toString = real_part + " + " + imaginary_part + "i"
}
We have defined two identical methods called * and multiply doing the same multiplication of complex numbers as per formula (1) above. The toString is the Scala implementation equivalent to toString in Java to return a more meaningful object description.
Let us then run below commands to validate our code:
val x = new ComplexNumber(1.0,2.0)
val y = new ComplexNumber(3.0,4.0)
x*y
x.*(y)
x.multiply(y)
The results are shown below:
scala> class ComplexNumber(val real_part : Double, val imaginary_part : Double) {
|
| def *(Second_Operand: ComplexNumber) = {
| new ComplexNumber(this.real_part*Second_Operand.real_part - this.imaginary_part*Second_Operand.imaginary_part, this.real_part*Second_Operand.imaginary_part + Second_Operand.real_part*this.imaginary_part)
| }
|
| def multiply(Second_Operand: ComplexNumber) = {
| new ComplexNumber(this.real_part*Second_Operand.real_part - this.imaginary_part*Second_Operand.imaginary_part, this.real_part*Second_Operand.imaginary_part + Second_Operand.real_part*this.imaginary_part)
| }
| override def toString = real_part + " + " + imaginary_part + "i"
| }
defined class ComplexNumber
scala> val x = new ComplexNumber(1.0,2.0)
x: ComplexNumber = 1.0 + 2.0i
scala> val y = new ComplexNumber(3.0,4.0)
y: ComplexNumber = 3.0 + 4.0i
scala> x*y
res56: ComplexNumber = -5.0 + 10.0i
scala> x.*(y)
res57: ComplexNumber = -5.0 + 10.0i
scala> x.multiply(y)
res58: ComplexNumber = -5.0 + 10.0i
The results are in line with formula (1).
Let us now rewrite the code so that the multiplication operation works as prefix notation.
class ComplexNumber1(val real_part : Double, val imaginary_part : Double){
override def toString = real_part + " + " + imaginary_part + "i"
}
def *(First_Operand: ComplexNumber1,Second_Operand: ComplexNumber1):ComplexNumber1 = {
val a:Double = First_Operand.real_part*Second_Operand.real_part - First_Operand.imaginary_part*Second_Operand.imaginary_part;
val b:Double = First_Operand.real_part*Second_Operand.imaginary_part + Second_Operand.real_part*First_Operand.imaginary_part;
new ComplexNumber1(a,b)}
def multiply(First_Operand: ComplexNumber1,Second_Operand: ComplexNumber1):ComplexNumber1 = {
val a:Double = First_Operand.real_part*Second_Operand.real_part - First_Operand.imaginary_part*Second_Operand.imaginary_part;
val b:Double = First_Operand.real_part*Second_Operand.imaginary_part + Second_Operand.real_part*First_Operand.imaginary_part;
new ComplexNumber1(a,b)}
The results are shown below:
scala> class ComplexNumber1(val real_part : Double, val imaginary_part : Double){
| override def toString = real_part + " + " + imaginary_part + "i"
| }
defined class ComplexNumber1
scala>
scala> def *(First_Operand: ComplexNumber1,Second_Operand: ComplexNumber1):ComplexNumber1 = {
| val a:Double = First_Operand.real_part*Second_Operand.real_part - First_Operand.imaginary_part*Second_Operand.imaginary_part;
| val b:Double = First_Operand.real_part*Second_Operand.imaginary_part + Second_Operand.real_part*First_Operand.imaginary_part;
| new ComplexNumber1(a,b)}
$times: (First_Operand: ComplexNumber1, Second_Operand: ComplexNumber1)ComplexNumber1
scala>
scala> def multiply(First_Operand: ComplexNumber1,Second_Operand: ComplexNumber1):ComplexNumber1 = {
| val a:Double = First_Operand.real_part*Second_Operand.real_part - First_Operand.imaginary_part*Second_Operand.imaginary_part;
| val b:Double = First_Operand.real_part*Second_Operand.imaginary_part + Second_Operand.real_part*First_Operand.imaginary_part;
| new ComplexNumber1(a,b)}
multiply: (First_Operand: ComplexNumber1, Second_Operand: ComplexNumber1)ComplexNumber1
Let us test the above code with the same values that we used earlier and test the code:
val x = new ComplexNumber1(1.0,2.0)
val y = new ComplexNumber1(3.0,4.0)
*(x,y)
multiply(x,y)
The results are shown below:
scala> val x = new ComplexNumber1(1.0,2.0)
x: ComplexNumber1 = 1.0 + 2.0i
scala> val y = new ComplexNumber1(3.0,4.0)
y: ComplexNumber1 = 3.0 + 4.0i
scala> *(x,y)
res60: ComplexNumber1 = -5.0 + 10.0i
scala> multiply(x,y)
res61: ComplexNumber1 = -5.0 + 10.0i
The results are identical to the results from the earlier code.
Unary operators like +,-,!, or ~ can also be customized similar to the infix operators above. For example, we can find the absolute value of an integer as shown below:
class Absolute(val i:Int){
def unary_! = if (i<0) -i else i
}
Note that to use ! as an operator, we have to use unary_! as the method name. To test we can use below code:
var x = new Absolute(1)
var y = new Absolute(-1)
!x
!y
The results are shown below:
scala> class Absolute(val i:Int){
| def unary_! = if (i<0) -i else i
| }
defined class Absolute
scala> var x = new Absolute(1)
x: Absolute = Absolute@20db19ff
scala> var y = new Absolute(-1)
y: Absolute = Absolute@578d5d02
scala> !x
res72: Int = 1
scala> !y
res73: Int = 1
In the last segment, we see codes for ++ and -- which are missing in Scala. The codes are shown for ++ and -- as a prefix unary operator below:
def ++(i:Int) = i + 1
def --(i:Int) = i - 1
The code to test it is below:
++(1)
--(1)
--(++(1))
The results are shown below:
scala> def ++(i:Int) = i + 1
$plus$plus: (i: Int)Int
scala> def --(i:Int) = i - 1
$minus$minus: (i: Int)Int
scala> ++(1)
res77: Int = 2
scala> --(1)
res78: Int = 0
scala> --(++(1))
res79: Int = 1
This concludes the Operator Overloading topic.
All operators like + , - , * , / , % in Scala mean the same as in other languages as Java and follow the infix notation. Infix notation means that the operator in placed in between the operands. For example, for multiplying two integers integer_1 and integer_2, we write:
integer_1 * integer_2
The other two notations are prefix notation and postfix notation. In prefix notation, the operator is placed before the operands, and in postfix notation, the operator is placed after the operands.
In Scala, all operators are methods. So, a multiplication operation between two integers can be written as:
3 * 5
or
3.*(5)
The results are shown below:
scala> 3 * 5
res42: Int = 15
scala> 3 . * (5)
res43: Int = 15
Since operators are methods in Scala, we can create a method, say, with name, * , to mean something different but imply the same product spirit in a new context. Let us see the program for multiplication of complex numbers. It is not straightforward as any complex number has a real part and an imaginary part. The formula for the product of complex numbers is shown below:
(a + bi) * (c + di) = (ac-bd) + (ad + bc)i ---------------------------- (1)
This can be implemented in Scala by first defining a complex number class that takes two parameters, one for the real part and another for the imaginary part. Then, we can define a method with the symbol * that will return the product of the multiplication operation between the two complex numbers.
The class with the real and imaginary parameters is defined below:
class ComplexNumber(val real_part : Double, val imaginary_part : Double)
We add the multiplication method as shown below:
class ComplexNumber(val real_part : Double, val imaginary_part : Double) {
def *(Second_Operand: ComplexNumber) = {
new ComplexNumber(this.real_part*Second_Operand.real_part - this.imaginary_part*Second_Operand.imaginary_part, this.real_part*Second_Operand.imaginary_part + Second_Operand.real_part*this.imaginary_part)
}
def multiply(Second_Operand: ComplexNumber) = {
new ComplexNumber(this.real_part*Second_Operand.real_part - this.imaginary_part*Second_Operand.imaginary_part, this.real_part*Second_Operand.imaginary_part + Second_Operand.real_part*this.imaginary_part)
}
override def toString = real_part + " + " + imaginary_part + "i"
}
We have defined two identical methods called * and multiply doing the same multiplication of complex numbers as per formula (1) above. The toString is the Scala implementation equivalent to toString in Java to return a more meaningful object description.
Let us then run below commands to validate our code:
val x = new ComplexNumber(1.0,2.0)
val y = new ComplexNumber(3.0,4.0)
x*y
x.*(y)
x.multiply(y)
The results are shown below:
scala> class ComplexNumber(val real_part : Double, val imaginary_part : Double) {
|
| def *(Second_Operand: ComplexNumber) = {
| new ComplexNumber(this.real_part*Second_Operand.real_part - this.imaginary_part*Second_Operand.imaginary_part, this.real_part*Second_Operand.imaginary_part + Second_Operand.real_part*this.imaginary_part)
| }
|
| def multiply(Second_Operand: ComplexNumber) = {
| new ComplexNumber(this.real_part*Second_Operand.real_part - this.imaginary_part*Second_Operand.imaginary_part, this.real_part*Second_Operand.imaginary_part + Second_Operand.real_part*this.imaginary_part)
| }
| override def toString = real_part + " + " + imaginary_part + "i"
| }
defined class ComplexNumber
scala> val x = new ComplexNumber(1.0,2.0)
x: ComplexNumber = 1.0 + 2.0i
scala> val y = new ComplexNumber(3.0,4.0)
y: ComplexNumber = 3.0 + 4.0i
scala> x*y
res56: ComplexNumber = -5.0 + 10.0i
scala> x.*(y)
res57: ComplexNumber = -5.0 + 10.0i
scala> x.multiply(y)
res58: ComplexNumber = -5.0 + 10.0i
The results are in line with formula (1).
Let us now rewrite the code so that the multiplication operation works as prefix notation.
class ComplexNumber1(val real_part : Double, val imaginary_part : Double){
override def toString = real_part + " + " + imaginary_part + "i"
}
def *(First_Operand: ComplexNumber1,Second_Operand: ComplexNumber1):ComplexNumber1 = {
val a:Double = First_Operand.real_part*Second_Operand.real_part - First_Operand.imaginary_part*Second_Operand.imaginary_part;
val b:Double = First_Operand.real_part*Second_Operand.imaginary_part + Second_Operand.real_part*First_Operand.imaginary_part;
new ComplexNumber1(a,b)}
def multiply(First_Operand: ComplexNumber1,Second_Operand: ComplexNumber1):ComplexNumber1 = {
val a:Double = First_Operand.real_part*Second_Operand.real_part - First_Operand.imaginary_part*Second_Operand.imaginary_part;
val b:Double = First_Operand.real_part*Second_Operand.imaginary_part + Second_Operand.real_part*First_Operand.imaginary_part;
new ComplexNumber1(a,b)}
The results are shown below:
scala> class ComplexNumber1(val real_part : Double, val imaginary_part : Double){
| override def toString = real_part + " + " + imaginary_part + "i"
| }
defined class ComplexNumber1
scala>
scala> def *(First_Operand: ComplexNumber1,Second_Operand: ComplexNumber1):ComplexNumber1 = {
| val a:Double = First_Operand.real_part*Second_Operand.real_part - First_Operand.imaginary_part*Second_Operand.imaginary_part;
| val b:Double = First_Operand.real_part*Second_Operand.imaginary_part + Second_Operand.real_part*First_Operand.imaginary_part;
| new ComplexNumber1(a,b)}
$times: (First_Operand: ComplexNumber1, Second_Operand: ComplexNumber1)ComplexNumber1
scala>
scala> def multiply(First_Operand: ComplexNumber1,Second_Operand: ComplexNumber1):ComplexNumber1 = {
| val a:Double = First_Operand.real_part*Second_Operand.real_part - First_Operand.imaginary_part*Second_Operand.imaginary_part;
| val b:Double = First_Operand.real_part*Second_Operand.imaginary_part + Second_Operand.real_part*First_Operand.imaginary_part;
| new ComplexNumber1(a,b)}
multiply: (First_Operand: ComplexNumber1, Second_Operand: ComplexNumber1)ComplexNumber1
Let us test the above code with the same values that we used earlier and test the code:
val x = new ComplexNumber1(1.0,2.0)
val y = new ComplexNumber1(3.0,4.0)
*(x,y)
multiply(x,y)
The results are shown below:
scala> val x = new ComplexNumber1(1.0,2.0)
x: ComplexNumber1 = 1.0 + 2.0i
scala> val y = new ComplexNumber1(3.0,4.0)
y: ComplexNumber1 = 3.0 + 4.0i
scala> *(x,y)
res60: ComplexNumber1 = -5.0 + 10.0i
scala> multiply(x,y)
res61: ComplexNumber1 = -5.0 + 10.0i
The results are identical to the results from the earlier code.
Unary operators like +,-,!, or ~ can also be customized similar to the infix operators above. For example, we can find the absolute value of an integer as shown below:
class Absolute(val i:Int){
def unary_! = if (i<0) -i else i
}
Note that to use ! as an operator, we have to use unary_! as the method name. To test we can use below code:
var x = new Absolute(1)
var y = new Absolute(-1)
!x
!y
The results are shown below:
scala> class Absolute(val i:Int){
| def unary_! = if (i<0) -i else i
| }
defined class Absolute
scala> var x = new Absolute(1)
x: Absolute = Absolute@20db19ff
scala> var y = new Absolute(-1)
y: Absolute = Absolute@578d5d02
scala> !x
res72: Int = 1
scala> !y
res73: Int = 1
In the last segment, we see codes for ++ and -- which are missing in Scala. The codes are shown for ++ and -- as a prefix unary operator below:
def ++(i:Int) = i + 1
def --(i:Int) = i - 1
The code to test it is below:
++(1)
--(1)
--(++(1))
The results are shown below:
scala> def ++(i:Int) = i + 1
$plus$plus: (i: Int)Int
scala> def --(i:Int) = i - 1
$minus$minus: (i: Int)Int
scala> ++(1)
res77: Int = 2
scala> --(1)
res78: Int = 0
scala> --(++(1))
res79: Int = 1
This concludes the Operator Overloading topic.