Function Currying
Currying transforms a function with multiple arguments into a series of functions each taking a single argument. This technique essentially creates closures.
package com.example.functions
object CurryingExample {
def main(args: Array[String]): Unit = {
// Basic function to find the larger of two integers
def largerValue(x: Int, y: Int): Int = if (x > y) x else y
println(largerValue(3, 5))
// Higher-order function using currying
def largerCurried(first: Int) = {
def inner(second: Int): Int = if (first > second) first else second
inner _
}
val partial = largerCurried(4)
println(partial(2))
println(partial(1))
println(largerCurried(5)(7))
// Simplified curried function definition
def curriedLarger(first: Int)(second: Int) = if (first > second) first else second
println(curriedLarger(4)(3))
val result = curriedLarger(3)(5)
println(result)
}
}
Call-by-Name Parameters
Call-by-name parameters evaluate the argument expression each time it is referenced within the function.
package com.example.functions
object CallByNameDemo {
def main(args: Array[String]): Unit = {
// Accepts any block yielding an Int
def doubleEval(expr: => Int): Unit = println(expr)
doubleEval(3)
doubleEval(if (3 > 5) 3 else 5)
def sum(a: Int, b: Int): Int = a + b
doubleEval(sum(3, 5))
// Requires a function of type (Int, Int) => Int
def compute(func: (Int, Int) => Int): Unit = println(func)
compute(sum)
}
}
Implementing a custom while loop using call-by-name parameters:
package com.example.functions
object CustomLoop {
def main(args: Array[String]): Unit = {
def customWhile(predicate: => Boolean)(action: => Unit): Unit = {
if (predicate) {
action
customWhile(predicate)(action)
}
}
var counter = 1
customWhile(counter <= 10) {
println(counter)
counter += 1
}
}
}
Lazy Evaluation
Lazy evaluation delays computation until the value is actually needed.
package com.example.functions
object LazyEvaluation {
def main(args: Array[String]): Unit = {
def subtract(x: Int, y: Int): Int = {
println("Executing subtraction")
x - y
}
val immediate = subtract(6, 3)
println("-" * 50)
lazy val deferred = subtract(7, 2)
println(deferred)
}
}
Lazy can only be applied to val, not var.
Classes and Objects
Classes are defined using the class keyword.
package com.example.oop
object PersonExample {
def main(args: Array[String]): Unit = {
val person = new Person
person.setName("Alex")
person setAge 25
println(person.getName)
println(person.getAge)
println(person.identifier)
}
}
class Person {
var name: String = _
var age: Int = _
val identifier: String = "defaultId"
def setName(n: String): Unit = this.name = n
def getName = this.name
def setAge(a: Int) = this.age = a
def getAge = this.age
}
Empty classes can omit braces.
Constructors
Scala uses primary and auxiliary constructors.
package com.example.oop
object StudentExample {
def main(args: Array[String]): Unit = {
val student = new Student("Ella", 16, "TechSchool")
println(student.name)
println(student.school)
student.displayInfo()
val student2 = new Student("Leo", 12, 5)
println(student2.gradeLevel)
}
}
class Student(var name: String, age: Int, val school: String) {
var gradeLevel: Int = _
def this(name: String, age: Int, grade: Int) {
this(name, age, "TechSchool")
this.gradeLevel = grade
}
def displayInfo(): Unit = println(s"Name: $name, Age: $age, School: $school")
}
Access Modifiers
Scala provides private, protected, and default public access.
package com.example.oop
class Occupation {
val id: Long = 12865496L
protected var department: String = _
private var name: String = _
private[oop] var location: String = _
}
class Instructor extends Occupation {
def showDetails(): Unit = println(s"$id, $department")
}
Properties with Getters and Settters
package com.example.oop
object RectangleDemo {
def main(args: Array[String]): Unit = {
val rect = new Rectangle
rect.height = 5.2
println(rect.height)
rect.width = 4.5
println(rect.width)
println(rect.area)
}
}
class Rectangle {
private var _height: Double = _
private var _width: Double = _
def height: Double = _height
def height_=(h: Double): Unit = this._height = h
def width: Double = _width
def width_=(w: Double): Unit = this._width = w
def area: Double = this._height * this._width
}
Packages and Imports
Packages can be defined in multiple styles.
package com.example.utils
import java.util.{ArrayList, List => JList, _}
import scala.collection.mutable.{Map => MutableMap}
object PackageExample {
def main(args: Array[String]): Unit = {
val list = new ArrayList[String]()
}
}
Package Objects
Package objects allow shared definitions within a package.
package com.example.shared
package utilities
object PackageObjectDemo {
def main(args: Array[String]): Unit = {
println(utilities.multiply(4, 5))
println(utilities.baseValue)
}
}
package object utilities {
val baseValue = 100
def multiply(x: Int, y: Int): Int = x * y
}
Inheritance and Polymorphism
Scala supports single inheritance with extends.
package com.example.inheritance
object InheritanceExample {
def main(args: Array[String]): Unit = {
val learner: Learner = new Beginner
learner.learn()
println(learner.serialVersion)
println(learner.isInstanceOf[Beginner])
val beginner = learner.asInstanceOf[Beginner]
}
}
class Learner {
val serialVersion: Long = 54896524L
def learn(): Unit = println("Learning")
}
class Beginner extends Learner {
override val serialVersion: Long = 3248203578L
override def learn(): Unit = println("Beginner is learning")
}