scala part II
- scala runs on top of jvm
- scala is like java so requires main, or we can
extends App
then we dont have to define main method
//define main method
object example {
def main(args: Array[]) = {
println("hello")
}
}
//define without main method
object example extends App{
println("hello")
}
//variable length arguments
object example extends App{
func("a", "b", "c")
def func(name: String*) = {
for(i <- name) {
println(i)
}
}
}
difference between Nil, null, None, Nothing, Option, Unit
Null
Null is a trait in scala, only one instance of Null exists that is null, we should restrict use of null beacuse it can lead to null pointer exception.
object example extends App{
//func("hello") //it will fail
func(null) //only this will work as input can only be null
def func(a: Null): Unit = {println("a")}
}
Nil
It is an empty list.
object example extends App{
val c = Nil
println(c)
}
Nothing
Its trait, without any instance. Nothing means there was an exception and nothing was returned.
object example extends App{
def func() = {
throw new Exception
}
}
Option
In a function if no useful value to return. null return is not preferred. There is inbuilt solution for this
object example extends App{
def func(n: Int): Option[String] = {
if(n >= 0) Some("value")
else None
}
def printfunc(n: Int) = {
func(n) match {
case Some(str) => println(str)
case None => println("None data")
}
}
printfunc(5) //output: value
printfunc(-1) //output: None data
}
Unit
- It is like void in java
- Nothing means there was error and nothing return
- Unit means there are side effects
object example extends App{
def func() = {
println("hello")
}
}
Alternate of null
we should not use null
as it can lead to null pointer exception. alternate is to use Option
//null pointer exception issue
object example extends App{
class Abc(n: Int) {
var a: String = null
}
val v = new Abc(1)
println(v.a.length)
}
//gracefull way of handling no data and avoid null point exception
object example extends App{
class Abc(n: Int) {
var a: Option[String] = None
}
val v = new Abc(1)
println(v.a.getOrElse("no data"))
v.a = Some("new data")
println(v.a.getOrElse("no data"))
}
yield
val a = for(i <- 1 to 10) {
i*i
}
println(a) //output: ()
val b = for(i <- 1 to 10) yield {
i*i
}
println(b) //output: Vector(1, 4, 9, 16, 25, 36, 49, 64, 81, 100)
vector
- mix of Array & List
- provide index support as Array
- immutability as List
if guard
same code above if done for alternate vaules produces all output and where codintion in loop does not match there it returns Unit
. This can be solved using if guard
val a = for(i <- 1 to 10) yield {
if(i%2==0) i*i
}
println(a) //output: Vector((), 4, (), 16, (), 36, (), 64, (), 100)
val b = for(i <- 1 to 10; if i%2==0) yield {
i*i
}
println(b) //output: Vector(4, 16, 36, 64, 100)
pattern guard
case statements can be combined with if guards
to provide extra logic during pattern matching.
def func(c: Int) {
c match {
case a if a>=0 => println("+")
case b if b<0 => println("-")
}
}
func(10) //output: +
func(-1) //output: -
Note: whenever pattern guard is used then we have to cover all conditions in case case if not then we might run into exceptions
for comprehension
each for loop
converted to comprehension as shown below for better performance
for(i <- 1 to 10) println(i)
(1 to 10).foreach(println) //scala does this behind the scene "for comprehension"
difference between scala & java
- in java
==
is reference comparision, in scala it checks value - in scala
==
is same as.equals
as in scala only methods/functions are present, no operators - in jave
==
is operator but in scala it is method - for reference comparision in scala we can use eq
val a = "dummy"
val b = "dummy"
a==b //output: true
a.==(b) //output: true
a.equals(b) //output: true
a equals b //output: true
strict val vs lazy val
default val is strict and evaluated when declared, lazy val is evaluated during first use
//full code will be executed and last expression will be returned
val a = {
println("some print")
1
}
println(a) //output: 1
//full code will be executed when val is called in println
lazy val a = {
println("some print")
1
}
println(a)
//output:
//some print
//1
default packages
default packages imported:
- java.lang._
- scala._
- scala.Predef._
example: to use Math
we dont have to import any package as it is by default imported.
scala apply
apply closes gap between object and function paradigme in scala, we can call an object like a function.
So in singleton object if we have apply
method then that can be called directly by object as a function without apply
method name.
object Abc {
def apply(i: Int) {
println(s"$i is the value")
}
}
Abc.apply(1) //output: 1 is the value
Abc(1) //output: 1 is the value
diamond problem
in case of multiple inheritence, if same name of methods are used in parent classes then its a diamond problem, thats why scala does not support multiple inheritence.
class Abc {
def func() = println("abc")
}
class Def {
def func() = println("def")
}
//this is not supported and is diamond problem
class Ghi extends Abc, Def {
func
}
multiple inheritence can be done in scala by traits
trait A {
def func = println("A")
}
trait B extends A{
override def func = println("B")
}
object C extends A with B
func //output: B
order here will be from right to left
type safe
code will be compiled and error out in case of type mismatch
val i: Int = "abc"
statically types vs dynamically types
statically typed
- type of variables are known at compile time
- example scala, java, c
- better performance
- no runtime errors
dynamically typed
- type of variables are checked at runtime
- like python
exception handling
exception: occurs due to some issue in code, example divide by zero error: occurs due to system issues, example OOM
try {
val a = 1/0
}
catch {
case e: Exception => println("manual exception")
}
finally {
println("last step")
}
monad
monad is object that wraps another object. output of calculation is input to other.
val l1 = List(1,2,3)
val l2 = List(4,5,6)
l1.flatMap { x => l2.map {y => x + y} }
//output: List(5, 6, 7, 6, 7, 8, 7, 8, 9)
ofDim
used to create multi dimensional array
val a = Array.ofDim[Int](2,2)
a(0)(0) = 1
for(i <- 0 to 1; j <- 0 to 1) println(a(i)(j))
design patterns
singleton design pattern
restricts instansiation of class to one object and provide global access to it.
object a {
//class level functionality
}
lazy initialization
initialization of instance on first access, to avaid expensive computation
val x = {
println("a")
1
}
lazy val x = {
println("a")
1
}
x
diff between Array & ArrayBuffer
- both are mutable
- ArrayBuffer is resizable but Array isn’t
- if we appand ArrayBuffer it gets larger but if append Array it will internally create new Array some performance hit
Comments