`scala.collection.immutable`
package and provides various operations for working with key-value pairs. Here are some key characteristics and features of Scala Map:get
` method to retrieve the value associated with a key. It returns an `Option
` type, which allows for handling scenarios where the key may not exist in the Map.+
` operator or the `updated
` method. Both methods return a new Map with the desired modifications.-
` operator or the `removed
` method. Similarly, these methods return a new Map with the specified element removed.HashMap
, LinkedHashMap
, and TreeMap
.val map = Map("name" -> "John", "age" -> 30, "city" -> "London")
println(map("name")) // Output: John
val updatedMap = map + ("age" -> 31) // Updating an existing key-value pair
val newMap = map + ("country" -> "UK") // Adding a new key-value pair
val removedMap = map - "city" // Removing an element based on the key
map.foreach { case (key, value) =>
println(s"Key: $key, Value: $value")
}​
List
, Set
, and Map
. Here are the key differences between these collections:head
`, `tail
`, `prepend
`, `append
`, and more for manipulating elements.contains
`, `intersect
`, `union
`, `diff
`, and more for set manipulation.
3. Map :get
`, `getOrElse
`, `contains
`, `keys
`, `values
`, and more for map manipulation. val age: Int = 30​
age
` is an immutable variable that holds the value 30. It cannot be reassigned to a different value. val list: List[Int] = List(1, 2, 3)
val updatedList = list :+ 4 // Creates a new list with 4 appended​
list
` is an immutable List, and the `:+
` operator returns a new List with 4 appended. The original list remains unchanged. case class Person(name: String, age: Int)
val john = Person("John", 30)
val olderJohn = john.copy(age = 31) // Creates a new Person with age modified​
copy
` creates a new Person object with the age modified. The original object `john
` remains unchanged. equals()
, hashcode()
, and toString(
). case class className(parameters)​
case class Student(name:String, age:Int)
object MainObject
{
def main(args:Array[String])
{
var c = Student(“Visthara”, 23)
println("Student name:" + c.name);
println("Student age: " + c.age);
}
} ​
Student Name: Visthara
Student Age: 23​
+, -, *, /, %,
etc. They can be used as method names but must be surrounded by backticks (`)
.(_)
. They can be used as anonymous placeholders for unused parameters or values. def operateOnNumbers(a: Int, b: Int, operation: (Int, Int) => Int): Int = {
operation(a, b)
}
val add: (Int, Int) => Int = (a, b) => a + b
val subtract: (Int, Int) => Int = (a, b) => a - b
val result1 = operateOnNumbers(5, 3, add) // Passes the 'add' function as an argument
val result2 = operateOnNumbers(7, 2, subtract) // Passes the 'subtract' function as an argument
println(result1)
println(result2) ​
Output :
8 //result1
5 //result2​
In this example, `operateOnNumbers
` is a higher-order function that takes two integer values `a
` and `b
`, along with an operation function as arguments. The operation function is defined as `(Int, Int) => Int
`, meaning it takes two integers and returns an integer.operateOnNumbers
` function applies the operation function to the provided arguments `a
` and `b
` and returns the result. The `add
` and `subtract
` functions are defined as separate functions that match the required `(Int, Int) => Int
` function signature.add
` and `subtract
`) to the `operateOnNumbers` higher-order function, we can perform different operations on the input numbers. val str = 1 #:: 2 #:: 3 #:: Stream.empty​
::
operator, while you can build Streams by using the #::
operator with Stream.empty at the end of the expression. A stream's head in the above syntax is 1, while its tail is 2 and 3. object MainObject
{
def main(args:Array[String]){
val stream = 20 #:: 30 #:: 45 #:: Stream.empty
println(stream)
}
} ​
Stream(20, ?) ​
var Variable_name: Data_type = "value"; ​
Example : var Company: String = "FreeTimeLearn”; ​
var Variable_name: Data_type = "value"; ​
Example : val Company: String = "FreeTimeLearn";​
tail position
" of the function. This enables the Scala compiler to optimize the recursive function by converting it into an iterative loop, eliminating the need for additional stack frames.@tailrec
`. When applied to a recursive function, the `@tailrec
` annotation helps ensure that the function is tail-recursive and triggers a compilation error if the function cannot be optimized.import scala.annotation.tailrec
def factorial(n: Int): Int = {
@tailrec
def factorialHelper(n: Int, acc: Int): Int = {
if (n <= 1)
acc
else
factorialHelper(n - 1, acc * n)
}
factorialHelper(n, 1)
}
val result = factorial(5)
println(result) // Output: 120​
In this example, the `factorial
` function calculates the factorial of a given number `n
`. The `factorialHelper
` function is defined inside the `factorial
` function and marked with the `@tailrec
` annotation.n
` for the current number and `acc
` for the accumulated result. It uses a recursive approach to calculate the factorial by multiplying the current number with the accumulated result.factorialHelper(n - 1, acc * n)
` is the last operation performed in the function, it is tail-recursive. The `@tailrec
` annotation ensures that the function is optimized for tail recursion by the compiler.@tailrec
` annotation only guarantees the tail-recursive optimization if the function meets the required criteria. If the function doesn't fulfill the tail recursion conditions (such as having additional computations after the recursive call), the annotation will result in a compilation error. ofDim()
is a method in Scala that lets us create multidimensional arrays. Since these let us store data in more than one dimension, we can store data like in a matrix. scala> import Array.ofDim
import Array.ofDim
scala> var a=ofDim[Int](3,3)
a: Array[Array[Int]] = Array(Array(0, 0, 0), Array(0, 0, 0), Array(0, 0, 0))
scala> var k=1
k: Int = 1
scala> for(i<-0 to 2){
| for(j<-0 to 2){
| a(i)(j)={i+k}
| k+=1
| }
| k-=1
| }
scala> a​
Array[Array[Int]] = Array(Array(1, 2, 3), Array(4, 5, 6), Array(7, 8, 9)).
scala> import scala.collection.immutable._
import scala.collection.immutable._
scala> var nums=BitSet(7,2,4,3,1)
nums: scala.collection.immutable.BitSet = BitSet(1, 2, 3, 4, 7)
scala> nums+=9 //Adding an element
scala> nums​
scala.collection.immutable.BitSet = BitSet(1, 2, 3, 4, 7, 9)
scala> nums-=4 //Deleting an element
scala> nums​
scala.collection.immutable.BitSet = BitSet(1, 2, 3, 7, 9)
scala> nums-=0 //Deleting an element that doesn’t exist
scala> nums​
scala.collection.immutable.BitSet = BitSet(1, 2, 3, 7, 9)
Data Type | Default Value | Size |
---|---|---|
Boolean | False | True or false |
Byte | 0 | 8 bit signed value (-27 to 27-1) |
Short | 0 | 16 bit signed value(-215 to 215-1) |
Char | '\u0000' | 16 bit unsigned Unicode character(0 to 216-1) |
Int | 0 | 32 bit signed value(-231 to 231-1) |
Long | 0L | 64 bit signed value(-263 to 263-1) |
Float | 0.0F | 32 bit IEEE 754 single-precision float |
Double | 0.0D | 64 bit IEEE 754 double-precision float |
String | Null | A sequence of characters |
def matchExample(x: Any): String = x match {
case 1 => "One"
case "hello" => "Greeting"
case true => "Boolean"
case List(1, 2, 3) => "List of 1, 2, 3"
case (a, b) => s"Tuple: $a, $b"
case _ => "Unknown"
}
val result1 = matchExample(1)
println(result1) // Output: One
val result2 = matchExample("hello")
println(result2) // Output: Greeting
val result3 = matchExample(true)
println(result3) // Output: Boolean
val result4 = matchExample(List(1, 2, 3))
println(result4) // Output: List of 1, 2, 3
val result5 = matchExample((4, 5))
println(result5) // Output: Tuple: 4, 5
val result6 = matchExample(10)
println(result6) // Output: Unknown​
matchExample
` function takes an argument `x
` of type `Any
` and performs pattern matching on it. The function matches `x
` against different cases using the `case
` keyword.x
` matches a specific case, the corresponding code block is executed. If none of the cases match, the underscore `_
` is used as a catch-all case to handle unknown or unmatched data.scala.reflect
” like “scala.reflect.BeanProperty
” in old versions. If we want to upgrade to new Scala versions, then we need to change this package from “scala.reflect
” to “scala.beans”. object MainObject {
def main(args: Array[String]) {
for( a <- 1 to 10 ){
println(a);
}
}
} ​
Scala.util.control.Breaks._ package
. It can break your code.import scala.util.control.Breaks._ // Importing package
object MainObject {
def main(args: Array[String]) {
breakable { // Breakable method to avoid exception
for(i<-1 to 10 by 2){
if(i==7)
break // Break used here
else
println(i)
}
}
}
} ​
val name = (27, "FreeTimeLearn") ​
Tuple2[Int, String]
. object FreeTimeLearn extends App
{
println("Hello Scala")
}​
unapply()
, as well as an optional method, apply()
. For mapping and unmapping data between form and model data, both apply and unapply methods are used. Apply()
method : Assembling an object from its components is done with this method. For example, by using the two components firstName and lastName, we can create the Employee object with the apply method.Unapply()
method : This method follows the reverse order of apply order and decomposes an object into components. For example, you can break or decompose that employee object down into its components i.e., firstName and lastName. val
` and `def
` are used to define values and methods, respectively. While both `val
` and `def
` are used to define named entities, they have distinct characteristics and purposes.val
` and `def
`:val
`: A `val
` defines a value that is computed once and assigned at the time of declaration. It is evaluated eagerly and the result is stored. The computed value remains constant throughout its lifetime. Think of `val` as a variable that cannot be reassigned.def
`: A `def
` defines a method, which is a reusable block of code that can be invoked with arguments. When a method is called, its body is evaluated and the result is returned. Methods are evaluated lazily, meaning the computation happens when the method is invoked.val
`: A `val
` is assigned a value once at the time of declaration and cannot be reassigned. It represents an immutable value. If you try to reassign a `val`, it will result in a compilation error.def
`: A `def
` is not directly assignable or reassignable. It represents a callable block of code that can be invoked with different arguments. You can think of `def` as a function or method declaration.val
`: A `val
` allocates memory space for storing the computed value at the time of declaration. The allocated memory is released when the variable goes out of scope.def
`: A `def
` does not allocate memory space for the method itself. Instead, it defines a code block that is evaluated when the method is called.val
`: A `val
` is evaluated eagerly at the time of declaration. The value is computed and assigned immediately.def
`: A `def
` is evaluated lazily, meaning the computation is deferred until the method is called. The code inside the method body is executed when the method is invoked.
5. Use Cases :val
`: Use `val
` when you want to define an immutable value that is computed once and remains constant.def
`: Use `def
` when you want to define a reusable block of code that can be invoked with different arguments. Methods allow for code abstraction and reuse.val
` and `def
`:val x: Int = {
println("Computing x")
42
}
def y: Int = {
println("Computing y")
42
}
println(x) // Output: Computing x, 42
println(x) // Output: 42 (value is not recomputed)
println(y) // Output: Computing y, 42
println(y) // Output: Computing y, 42 (method body is evaluated each time)​
x
` is a `val
` that is computed eagerly at the time of declaration. The value is assigned once and remains constant throughout its lifetime. The println statement inside the `val
` block is executed only once.y
` is a `def
` that represents a method. The method body is evaluated each time the method is called. The println statement inside the method body is executed each time the method is invoked. Map()
and its close cousin flatMap()
are often used when we deal with data structures in Scala and both are higher-order functions. map()
method : It functions similarly to mapping operations in other languages like JavaScript since it allows us to iterate over a collection to translate it into another collection. Every element of the collection is transformed by applying a function to it. Its uses are heavily overloaded, making it useful for situations that aren't immediately obvious. Scala's map()
method is exceptionally powerful.flatpmap()
method : In Scala, flatMap()
is the same as map()
, except that flatMap removes the inner grouping of items and creates a sequence. Essentially, it is a combination of the flatten method and the map method. The map method followed by the flatten method produces the same result as flatMap()
, so we can say that flatMap runs the map method first, followed by the flatten method. Compared to the map method, flatMap is much more powerful. var x = 20
def function_name(y:Int)
{
println(x+y)
} ​
object FreeTimeLearn
{
def main(args: Array[String])
{
println( "Sum(2) value = " + sum(2))
println( "Sum(3) value = " + sum(3))
println( "Sum(4) value = " + sum(4))
}
var x = 20
val sum = (y:Int) => y + x
}​
Sum(2) value = 22
Sum(3) value = 23
Sum(4) value = 24​
var x = 20
is an impure closure. As can be seen, x is the same, and y is different. apply
` method is a special method that can be defined in a class or object. It is used to create an instance of the class or object using a concise syntax that resembles function invocation. The `apply
` method allows instances to be created without explicitly calling the constructor using the `new
` keyword.apply
` method :apply
` method in a class or object, you can use that class or object as if it were a function, creating new instances by invoking it with arguments.apply
` method provides a more compact and natural way to create instances by using parentheses and passing arguments, similar to calling a function.new
` keyword is omitted, the Scala compiler automatically invokes the `apply
` method when the class or object is used as a function call.apply
` methods can be defined with different parameter lists in the same class or object, allowing for method overloading based on argument types.apply
` method, you can make the creation of instances consistent with other operations performed on objects, such as method calls or property access.apply
` method :
class MyClass(val name: String, val age: Int)
object MyClass {
def apply(name: String, age: Int): MyClass = new MyClass(name, age)
}
val myObj = MyClass("John", 30)
println(myObj.name) // Output: John
println(myObj.age) // Output: 30​
MyClass
` object defines an `apply
` method that takes a name and age as arguments. Inside the `apply
` method, a new instance of `MyClass
` is created using the provided arguments.new
` keyword, the `MyClass
` object can be used as if it were a function. The `apply
` method is implicitly invoked with the provided arguments, and a new instance of `MyClass
` is created. This creates a more concise and intuitive syntax for object creation.apply
` method can be overloaded with different parameter lists, allowing for different ways to create instances of the class or object based on the arguments provided.apply
` method provides a convenient and expressive way to create instances of a class or object in Scala, enabling a more functional and concise coding style. Option
`, `Some
`, and `None
` are used to handle optional values and represent absence of a value. They are part of the Scala standard library and provide a safer alternative to handling null values.Option
`, `Some
`, and `None
`:Option
` is a container type that represents an optional value. It can either contain a value (`Some
`) or signify the absence of a value (`None
`). It is a generic type with two subclasses: `Some
` and `None
`.Some
` is a subclass of `Option
` and represents a container that holds a non-null value. It is used when a value is present.None
` is a subclass of `Option
` and represents the absence of a value. It is used to indicate that no value is present.Option
`, `Some
`, and `None
` helps in writing safer code by explicitly handling the absence of a value. It avoids null pointer exceptions that are common when working with nullable references.Option
` provides methods that allow for safe and concise composition of operations on optional values, such as `map
`, `flatMap`, and `getOrElse`.Option
` to handle the different cases of presence or absence of a value. It provides a convenient way to extract values from `Some
` and handle the `None
` case.
Here's an example to demonstrate the usage of `Option
`, `Some
`, and `None
`:val someValue: Option[String] = Some("Hello, World!")
val noneValue: Option[String] = None
def printValue(option: Option[String]): Unit = option match {
case Some(value) => println(value)
case None => println("No value")
}
printValue(someValue) // Output: Hello, World!
printValue(noneValue) // Output: No value​
someValue
` is an `Option
` holding a `Some
` value with the string "Hello, World!", while `noneValue
` is an `Option
` holding a `None
` value.printValue
` function takes an `Option
` as an argument and pattern matches against it. If the `Option
` is a `Some
`, the value is extracted and printed. If the `Option
` is a `None
`, it prints "No value
".Option
`, we can handle the cases where a value may or may not be present, explicitly indicating the absence of a value and avoiding null-related issues.Option
` and its subclasses (`Some
` and `None
`) promotes a more functional and safer approach to dealing with optional values in Scala, improving code clarity and reducing the likelihood of runtime errors caused by null references. object Main {
def sayHello () {
println ("Hello!");
}
}​
Main. SayHello ();​
If a singleton object has the same name as the class, then it is known as a Companion object and should be defined in the same source file as that of the class.class Main {
def sayHelloWorld() {
println("Hello World");
}
}
object Main {
def sayHello() {
println("Hello!");
}
}​
val
`, making the instances immutable. This ensures that the data contained in a case class cannot be modified after creation.copy
` method that allows you to create a copy of an instance with some or all of its fields modified. This is useful when you need to create a new instance that is similar to an existing instance but with some changes.apply
`, `unapply
`, and `toString
`. The `apply` method allows you to create instances of the case class without using the `new
` keyword.toString
` method, which prints a string representation of the case class instance, including the class name and its fields.
Here's an example that demonstrates the usage of a case class :case class Person(name: String, age: Int)
val person1 = Person("John", 30)
val person2 = Person("John", 30)
println(person1 == person2) // Output: true (structural equality)
println(person1.toString) // Output: Person(John,30)
val modifiedPerson = person1.copy(age = 35)
println(modifiedPerson) // Output: Person(John,35)​
Person
` case class is defined with `name
` and `age
` parameters. The case class automatically generates methods such as `equals
`, `hashCode
`, `toString
`, and `copy
`. We create two instances of the `Person
` case class with the same values for `name
` and `age
`, and they are considered equal due to structural equality.copy
` method to create a modified copy of a case class instance. In this example, we modify the `age
` field of `person1
` to 35, resulting in a new instance with the same `name
` but a different `age
`.<:
` symbol. For example, `A <: B
` means that the type parameter `A
` must be a subtype of `B
`. class Container[A <: Number] {
// ...
}
val container1 = new Container[Int] // Valid, Int is a subtype of Number
val container2 = new Container[String] // Invalid, String is not a subtype of Number​
Container
` class has an upper bound of `Number
`. This means that the type parameter `A
` can only be a subtype of `Number
` or the `Number
` class itself. Therefore, `Container[Int]
` is valid because `Int
` is a subtype of `Number
`, but `Container[String]
` is invalid because `String
` is not a subtype of `Number
`.
2. Lower Bounds : Lower bounds restrict the type parameter to be a supertype of a specified type. They are denoted by the `>:
` symbol. For example, `A >: B
` means that the type parameter `A
` must be a supertype of `B
`. class Printer[A >: String] {
// ...
}
val printer1 = new Printer[Any] // Valid, Any is a supertype of String
val printer2 = new Printer[Int] // Invalid, Int is not a supertype of String​
Printer
` class has a lower bound of `String
`. This means that the type parameter `A
` must be a supertype of `String
` or the `String
` class itself. Therefore, `Printer[Any]
` is valid because `Any
` is a supertype of `String
`, but `Printer[Int]
` is invalid because `Int
` is not a supertype of `String
`.List
`, `Option
`, and `Future
`. These types are not complete types on their own but require type parameters to become concrete types.trait Functor[F[_]] {
def map[A, B](fa: F[A])(f: A => B): F[B]
}
// Functor instance for List
implicit val listFunctor: Functor[List] = new Functor[List] {
def map[A, B](fa: List[A])(f: A => B): List[B] = fa.map(f)
}
// Functor instance for Option
implicit val optionFunctor: Functor[Option] = new Functor[Option] {
def map[A, B](fa: Option[A])(f: A => B): Option[B] = fa.map(f)
}
// Usage
val list: List[Int] = List(1, 2, 3)
val option: Option[Int] = Some(42)
val newList = Functor[List].map(list)(_ * 2)
val newOption = Functor[Option].map(option)(_ + 10)​
In this example, we define a `Functor
` type class that abstracts over types that can be mapped over. It takes a higher-kinded type `F[_]
` as a type parameter. The `map
` method in the `Functor
` trait takes an instance `fa
` of the type constructor `F
` and applies the function `f
` to transform the values within `F[A]
` to `F[B]`.Functor
` type class for the `List
` and `Option
` type constructors. The `map
` implementation for each type constructor is specific to that type, allowing us to map over lists and options using the `Functor
` abstraction.List
` and an `Option
`, and then use the `map
` method from the `Functor
` type class to transform the values within each container-like type. The `newList
` and `newOption
` variables contain the transformed values. trait Trait_Name
{
// Fields...
// Methods...
}
Example:
trait MyCompany
{
def company
def position
}
class MyClass extends MyCompany
{
def company()
{
println("Company: FreeTimeLearn")
}
def position()
{
println("Position: SoftwareDeveloper")
}
def employee() //Implementation of class method
{
println("Employee: Venkat")
}
}
object Main
{
def main(args: Array[String])
{
val obj = new MyClass();
obj.company();
obj.position();
Obj.employee();
}
} ​
Company: FreeTimeLearn
Position: SoftwareDeveloper
Employee: Venkat​
Java | Scala |
---|---|
It was originally designed to be an object-oriented language, but it began supporting functional programming features in recent years. | Originally intended to be both an object-oriented and functional language, Scala supports concurrency and immutability, as well as many other features of functional programming. |
It takes a short time to convert source code to byte code. | It takes a long time to compile the source code into byte code. |
In Java, even the most mundane and simple tasks require long-form code. | Scala is designed to be concise. One line of Scala code can easily replace twenty lines of “simple” Java code, even though it is a slightly more complex language. |
Lazy evaluation and operator overloading are not supported. | Lazy evaluation and operator overloading are supported. |
Due to Java's backward compatibility, code written in the new version will run without error in an older version. | The language does not provide backward compatibility. |
Grails, spring, and many other frameworks are supported. | Scala supports frameworks such as Play and Lift. |
Objects are treated as functions in Java. | In Scala, any method or function can be treated as a variable. |
Java variables are mutable by default. | Scala variables are immutable by default. |
When compared to Scala, Java is easier to read. | Unlike Java, Scala includes nested coding, making it less readable. |
Static keywords are used in Java. | There are no static keywords in Scala. |
Multiple inheritances are not supported by classes, but by interfaces. | The language supports multiple inheritances through classes, but not through abstract classes. |
println()
` and `print()
` functions are used to output text or values to the console. The main difference between them lies in how they handle the end of the line:println()
` : The `println()
` function adds a newline character (`\n
`) at the end of the output, which moves the cursor to the next line after printing the text. This means that each subsequent call to `println()
` will print on a new line. println("Hello")
println("World")​
Hello
World​
Hello
" is printed on one line, and then the cursor moves to the next line before printing "World
" on a new line.
2. `print()
` : The `print()
` function does not add a newline character at the end of the output. It simply prints the text or value without moving the cursor to the next line. If you use multiple `print()
` statements consecutively, they will all be printed on the same line. print("Hello ")
print("World")​
Hello World​
Hello
" and "World
" are printed on the same line because no newline character is added between them.println()
` and `print()
` in Scala is the presence or absence of a newline character at the end of the output. If you want to print on a new line, you can use `println()
`, and if you want to print on the same line, you can use `print()
`. The choice depends on your specific formatting requirements. while
` and `do-while
` loops are used for repetitive execution of code blocks, but they have a slight difference in their loop structure and behavior:while
` Loop : The `while` loop repeatedly executes a block of code as long as a given condition is true. It checks the condition before each iteration, and if the condition is false initially, the code block is not executed at all. var i = 0
while (i < 5) {
println(i)
i += 1
}​
0
1
2
3
4​
while
` loop iterates while `i
` is less than 5
. The `println(i)
` statement is executed for each iteration, and `i
` is incremented by 1
. The loop terminates when the condition becomes false.
2. `do-while` Loop : The `do-while
` loop also repeatedly executes a block of code, but it checks the condition after each iteration. This means that the code block is executed at least once, regardless of the condition's initial value. var i = 0
do {
println(i)
i += 1
} while (i < 5)​
0
1
2
3
4​
do-while
` loop starts by executing the code block, which includes the `println(i)
` statement. Then, it checks the condition `i < 5
`. If the condition is true, the loop continues to iterate. Otherwise, it terminates.while
` loop, the condition is checked before the first iteration, while in the `do-while
` loop, the condition is checked after the first iteration.while
` and `do-while
` loops are useful for scenarios where you want to repeat a code block until a certain condition is met. However, it's important to ensure that the condition eventually becomes false to avoid infinite loops. (z:Int, y:Int)=> z*y
Or
(_:Int)*(_Int)​
def curryfunction_name(argument1, argument2) = operation ​
object Currying
{
def add(a: Int, b: Int) = a + b;
def main(args: Array[String])
{
println(add(10, 5));
}
} ​
15 ​
class class_name(Parameter_list)
{
// Statements...
} ​
def this(......)​
def func1(implicit a : Int) // a is implicit
def func2(implicit a : Int, b : Int) // a and b both are implicit
def func3 (a : Int)(implicit b : Int) // only b is implicit
Example:
object InterviewBit
{
def main(args: Array[String])
{
val value = 20
implicit val addition = 5
def add(implicit to: Int) = value + to
val result = add
println(result)
}
} ​
Output : 25​
= (equal)
operator. If you use it, the function will return value. If you don't use it, your function will not return anything and will work like the subroutine.object MainObject {
def main(args: Array[String]) {
var result = functionExample() // Calling function
println(result)
}
def functionExample() = { // Defining a function
var a = 10
a
}
} ​
class Arithmetic{
def add(a:Int, b:Int){
var sum = a+b
println(sum)
}
def add(a:Int, b:Int, c:Int){
var sum = a+b+c
println(sum)
}
}
object MainObject{
def main(args:Array[String]){
var a = new Arithmetic();
a.add(10,10);
a.add(10,10,10);
}
} ​
extends
` keyword. When a class extends another class, it inherits all the members (methods and fields) of the superclass. The subclass can then add or override these inherited members to customize its behavior.class Animal {
def eat(): Unit = {
println("The animal is eating.")
}
}
class Dog extends Animal {
override def eat(): Unit = {
println("The dog is eating.")
}
def bark(): Unit = {
println("Woof!")
}
}
val animal: Animal = new Animal()
animal.eat() // Output: The animal is eating.
val dog: Dog = new Dog()
dog.eat() // Output: The dog is eating.
dog.bark() // Output: Woof!​
In this example, we have a base class `Animal
` and a subclass `Dog
` that extends `Animal
`. The `Animal
` class has a method `eat()
` that prints a generic message. The `Dog
` class overrides the `eat()
` method to provide a more specific implementation for dogs and adds a new method `bark()
`.eat()
` and `bark()
` methods. When we invoke `eat()
` on the `Animal
` instance, it uses the implementation from the `Animal
` class. However, when we invoke `eat()
` on the `Dog
` instance, it uses the overridden implementation from the `Dog
` class.println("How to extend abstract class Parent and define a sub-class of Parent called Child")
class Child=(name:String)extends Parent(name){
override def printName:Unit= println(name)
}
object Child {
def apply(name:String):Parent={
new Child(name)
}
}​
import akka.actor.{Actor, ActorSystem, Props}
// Define an actor class
class MyActor extends Actor {
def receive: Receive = {
case message: String => println(s"Received message: $message")
case _ => println("Unknown message")
}
}
// Create an ActorSystem
val system = ActorSystem("MySystem")
// Create an instance of MyActor
val actor = system.actorOf(Props[MyActor], "myActor")
// Send messages to the actor
actor ! "Hello"
actor ! "World"
// Terminate the ActorSystem
system.terminate()​
In this example, we define a simple actor class `MyActor
` that extends the `Actor
` trait. The actor overrides the `receive
` method, which defines the behavior of the actor when it receives messages. In this case, it prints the received message.ActorSystem
` using `ActorSystem("MySystem")
`, which serves as the entry point for creating and managing actors. We create an instance of `MyActor
` using `system.actorOf(Props[MyActor], "myActor")
`, which returns an actor reference. We can send messages to the actor using the `!
` (bang) operator, as shown in `actor ! "Hello"
` and `actor !
"World"
`.ActorSystem
` using `system.terminate()
`.object
` keyword has multiple roles and serves various purposes:object
` keyword is used to define a named object, it creates a singleton instance of a class. A singleton object is a class that has only one instance, and its definition and initialization occur simultaneously. Singleton objects are commonly used to hold utility methods, define entry points to applications, or provide global state or resources. object MySingleton {
def doSomething(): Unit = {
println("Doing something...")
}
}
MySingleton.doSomething()​
MySingleton
` is a singleton object that defines a method `doSomething()
`. The object can be accessed directly, and its methods can be invoked without the need for instantiation. This allows for a convenient and concise way to organize utility methods or access shared resources. class MyClass(name: String)
object MyClass {
def apply(name: String): MyClass = new MyClass(name)
}
val myObject = MyClass("Example")​
In this example, the `MyClass
` class is defined, and its companion object provides a factory method `apply
` to create instances of `MyClass
`. By convention, the `apply
` method is a special method that can be called directly on the companion object, as shown in `MyClass("Example")
`. This syntax creates a new instance of `MyClass
` using the factory method defined in the companion object.object
` keyword can also be used to define entry points to Scala applications. When an object extends the `App
` trait, it provides a convenient way to define the entry point of the application without explicitly writing a `main
` method. object MyApp extends App {
println("Hello, Scala!")
}​
MyApp
` extends the `App
` trait, which comes with predefined behavior to execute the code inside the object. The code within the object's body is executed when the application is run. In this case, it simply prints "Hello, Scala!
" to the console.object
` keyword in Scala allows you to define singleton objects, companion objects, and entry points to applications. It provides a way to organize code, create singleton instances, define static-like behavior, and define entry points in a concise and convenient manner. var
`, `val
`, and `lazy val
` are used to declare variables, but they have different characteristics and behaviors:var
` : A variable declared with the `var
` keyword is mutable, meaning its value can be reassigned. It allows for changes to the variable's value throughout its lifetime. var x = 5
x = 10​
x
` is declared using `var
`, and its value is initially set to 5
. Later, it is reassigned to 10
using the `=
` operator. The value of a `var
` can be modified multiple times. val y = 7
// y = 10 // Error: Reassignment to val​
y
` is declared using `val
`, and its value is set to 7
. Attempting to reassign a value to a `val
` variable will result in a compilation error.
3. `lazy val
`: A `lazy val
` is similar to a `val
`, but its initialization is deferred until it is accessed for the first time. It allows for lazy evaluation, meaning the value is calculated only when it is needed, and subsequent accesses use the precomputed value. lazy val z = {
println("Initializing...")
42
}
println(z)
println(z)​
Initializing...
42
42​
z
` is declared as a `lazy val
` and is assigned an expression block that computes the value `42
`. The initialization block is executed only when `z
` is first accessed. In this case, the initialization block prints "Initializing...
" to the console. Subsequent accesses to `z
` reuse the precomputed value without reevaluating the initialization block.var
`, `val
`, and `lazy val
` depends on the requirements of your program and the desired behavior of the variable:var
` when you need a mutable variable that can be reassigned.val
` when you want an immutable variable or a constant that cannot be changed.lazy val
` when you want lazy evaluation, deferring the computation until the value is accessed for the first time.val
` and `lazy val
` for immutable variables can help improve code readability, maintainability, and reliability by reducing the potential for unintended modifications. Additionally, `lazy val
` can be useful for optimizing performance by deferring expensive computations until they are actually needed. sealed
` keyword in Scala is used to restrict the inheritance of classes or traits within a defined scope. When a class or trait is marked as `sealed
`, it means that it can only be extended within the same source file or compilation unit.sealed
` keyword is to enable pattern matching exhaustiveness checks at compile-time. When pattern matching is used with a `sealed
` class or trait, the compiler can analyze the patterns and provide warnings if there are any missing cases. This ensures that all possible cases are handled, reducing the likelihood of bugs or incomplete pattern matching logic.sealed
` keyword :sealed abstract class Fruit
case class Apple() extends Fruit
case class Banana() extends Fruit
def describeFruit(fruit: Fruit): String = fruit match {
case Apple() => "This is an apple."
case Banana() => "This is a banana."
}
val fruit: Fruit = Apple()
val description = describeFruit(fruit)
println(description) // Output: This is an apple.​
In this example, the `Fruit
` class is declared as `sealed
`, and it is extended by two case classes: `Apple
` and `Banana
`. The `describeFruit
` function uses pattern matching to provide descriptions for different kinds of fruits. Since the `Fruit
` class is `sealed
`, the compiler can check if all possible cases are handled in the pattern match.Orange
`, and not handle it in the pattern match, the compiler would generate a warning indicating that the pattern match is not exhaustive. This serves as a safety mechanism to catch potential bugs and ensure that all cases are explicitly handled.sealed
`, you can enforce a closed set of subclasses, enabling exhaustive pattern matching checks at compile-time. It helps improve code correctness, maintainability, and readability by making it easier to reason about all possible cases in a pattern match and avoiding unexpected behavior due to missing cases. yield
` keyword is used in a generator expression to construct a collection based on the elements generated by the expression. It allows you to transform and filter the elements produced by the generator expression and collect them into a new collection.for
` comprehension syntax, which combines `yield
` with one or more generator clauses and optional filter conditions. The `yield
` keyword specifies the expression to be generated for each element produced by the generators and filters.yield
` in a generator expression :val numbers = List(1, 2, 3, 4, 5)
val doubled = for (n <- numbers) yield n * 2
println(doubled) // Output: List(2, 4, 6, 8, 10)​
[1, 2, 3, 4, 5]
`. Using the `for
` comprehension with `yield
`, we iterate over each element in the `numbers
` list, and for each element `n
`, we yield `n * 2
`. The result is a new list `doubled
` that contains the doubled values of the original numbers.yield
` keyword captures the generated values and constructs a new collection of the same type as the generator expression. In this case, the generator expression is based on a list, so the result is also a list. However, the type of the result can vary depending on the type of the generator expression.yield
` keyword is useful when you want to transform or filter the elements produced by a generator expression and collect the results into a new collection. It provides a concise and readable way to express such transformations, making your code more expressive and maintainable. Seq
` or convert them to other collection types if needed.:_*
` syntax.// Java
public void printNames(String... names) {
for (String name : names) {
System.out.println(name);
}
}
printNames("John", "Jane", "Mike");​
// Scala
def printNames(names: String*): Unit = {
for (name <- names) {
println(name)
}
}
printNames("John", "Jane", "Mike")​
printNames
` method accepts a variable number of `String
` arguments. The method body iterates over the arguments and prints each name. trait TraitName
{
//Methods
//Fields
}​
->
Scala project and provide the project with a name.->
New ->
Scala Object and provide a name for your Scala application - >
Click Finish to create the file. object FreeTimeLearn
{ def main(args: Array[String])
{
println("Hello Scaler!!!")
}
} ​
Hello Scaler!!! ​
Option
`, `Either
`, and `Future
` types are examples of monads commonly used in functional programming. Libraries like Cats and Scalaz provide additional monadic abstractions and utilities to work with monads in a more expressive and powerful manner.Option
` monad in Scala :val maybeValue: Option[Int] = Some(42)
val result: Option[Int] = maybeValue.flatMap(value => {
if (value > 10) Some(value * 2)
else None
})
println(result) // Output: Some(84)​
maybeValue
` variable is an `Option[Int]
` that contains the value `42
`. We use the `flatMap
` method, provided by the `Option
` monad, to sequence a computation that doubles the value if it is greater than 10
. The result is `Some(84)
`.No value
” or “No Useful value
”. Unit is a final class defined in “scala
” package that is “scala.Unit
”. Unit is something similar to Java’s void. But they have few differences.()
()
is the one and only value of type Unit in Scala. However, there are no values of type void in Java.foreach
` and `map
` methods are available on collections, but they serve different purposes:`foreach(f: A => Unit): Unit`
foreach
` method is used to iterate over each element in a collection and apply a provided function `f` to each element.Unit
` val numbers = List(1, 2, 3, 4, 5)
numbers.foreach(num => println(num * 2))​
foreach
` method is called on the `numbers
` list, and for each element, the provided function `num => println(num * 2)
` is applied. It prints the doubled value of each number in the list.`map[B](f: A => B): List[B]`
map
` method is used to transform each element in a collection using a provided function `f` and return a new collection with the transformed elements.map
` is a new collection of the transformed elements. val numbers = List(1, 2, 3, 4, 5)
val doubledNumbers = numbers.map(num => num * 2)​
map
` method is called on the `numbers
` list, and for each element, the provided function `num => num * 2
` is applied to double the value. It returns a new list `doubledNumbers
` with the transformed elements.foreach
` and `map
` is their purpose and return value:foreach
` is used for iteration and applying a function with side effects to each element. It returns `Unit
` and is typically used when you want to perform actions on each element of the collection without producing a new collection.map
` is used for transformation and applying a function to each element, returning a new collection with the transformed elements. It does not have side effects and is useful when you want to transform the elements of a collection into a new collection.lazy
` keyword, which can be applied to both `val
` and `def
` declarations. When a value or method is declared as lazy, its initialization or execution is postponed until the first access, and the result is cached for subsequent accesses.lazy val expensiveComputation: Int = {
println("Performing expensive computation...")
// Expensive computation logic here
42
}
println("Before accessing the lazy value")
println(expensiveComputation) // Evaluation happens here
println("After accessing the lazy value")
println(expensiveComputation) // Evaluation is skipped, cached result is used​
In this example, the `expensiveComputation
` value is declared as lazy. When the program runs, the line `println("Before accessing the lazy value")
` is executed, but the actual computation of `expensiveComputation
` is not triggered yet. It is only evaluated when it is accessed for the first time, which happens on the line `println(expensiveComputation)
`. At this point, the output "Performing expensive computation..." is printed, indicating that the computation is being performed. The computed result (42) is then cached for subsequent accesses, as seen when `expensiveComputation` is accessed again.foldLeft
` and `reduceLeft
` methods are used to combine the elements of a collection in Scala. However, there is a slight difference in their behavior:foldLeft
` method :foldLeft[B](z: B)(op: (B, A) => B): B
`z
` and applying a binary operator `op
` to each element along with the accumulated result. val numbers = List(1, 2, 3, 4, 5)
val sum = numbers.foldLeft(0)((acc, num) => acc + num)​
foldLeft
` method is called on the `numbers
` list. The initial value `0
` is provided, and for each element, the binary operator `(acc, num) => acc + num
` is applied to accumulate the sum of the elements. The result is the sum of all the numbers.
2. `reduceLeft` method :reduceLeft(op: (A, A) => A): A
` val numbers = List(1, 2, 3, 4, 5)
val product = numbers.reduceLeft((acc, num) => acc * num)​
reduceLeft
` method is called on the `numbers
` list. The binary operator `(acc, num) => acc * num
` is applied to each pair of elements, starting from the first element, to calculate the product of all the numbers.foldLeft
` and `reduceLeft
` lies in the handling of the initial value:foldLeft
` requires an explicit initial value (`z
`) and applies the binary operator to both the initial value and each element.reduceLeft
` does not require an explicit initial value. Instead, it starts the computation with the first element of the collection and applies the binary operator successively to pairs of elements.foldLeft
`, you have more control over the initial value and can handle cases where the collection might be empty. On the other hand, `reduceLeft
` assumes that the collection has at least one element and uses its value as the starting point for the computation.