Navigating Complexity: The Role of Trusted Partners and VIAS3D in Dassault Sy...
Unit 4 notes.pdf
1. 191AIC302T-Object Oriented Programming with SCALA
Unit-IV
Department of AI & DSPage 1
Abstraction and Encapsulation
Abstraction: Abstract Methods - Abstract Class and its importance – implementation of
abstraction – Encapsulation: Packages - Access Modifiers – application of encapsulation -
Type Class and Conversions.
Abstraction
Abstraction is the process to hide the internal details and showing only the functionality. In
Scala, abstraction is achieved by using an abstract class. The working of the Scala abstract class
is similar to Java abstract class. In Scala, an abstract class is constructed using the abstract
keyword. It contains both abstract and non-abstract methods and cannot support multiple
inheritances. A class can extend only one abstract class.
Syntax:
abstract class class_name
{
// code..
}
The abstract methods of abstract class are those methods which do not contain any
implementation. Or in other words, the method which does not contain body is known as an
abstract method.
Syntax:
def function_name()
Example:
// Scala program to illustrate how to
// create an abstract class
// Abstract class
abstract class myauthor
{
// abstract method
def details()
}
2. 191AIC302T-Object Oriented Programming with SCALA
Unit-IV
Department of AI & DSPage 2
// GFG class extends abstract class
class GFG extends myauthor
{
def details()
{
println("Author name: Ankita Saini")
println("Topic name: Abstract class in Scala")
}
}
object Main
{
// Main method
def main(args: Array[String])
{
// objects of GFG class
var obj = new GFG()
obj.details()
}
}
Following are some important observations about abstract classes in Scala;
in Scala, we are not allowed to create the instance of the abstract class. If we try to create objects
of the abstract class, then the compiler will give an error as shown in the below program.
// Scala program to illustrate // the concept of abstract class // Abstract class
abstract class myauthor{
// abstract method
def details()
}
object Main {
// Main method
def main(args: Array[String]) {
// Object of myauthor class
var obj = new myauthor()
}
}
3. 191AIC302T-Object Oriented Programming with SCALA
Unit-IV
Department of AI & DSPage 3
In Scala, an abstract class can also contain fields. These fields are accessed by the abstract class
methods and by the methods of the class which inherit abstract class. As shown in the below
program.
// Scala program to illustrate
// the concept of abstract class
// Abstract class with fields
abstract class Geek
{
var name : String = "GeeksforGeeks"
var tutorial: String = "Scala"
def portal()
}
// GFG class extends abstract class
class GFG extends Geek
{
// Abstract class method accessing
// fields of the abstract class
def portal()
{
println("Portal name: " + name)
}
// GFG class method accessing
// fields of the abstract class
def tutdetails()
{
println("Tutorial name: " + tutorial)
}
}
object Main
{
// Main method
def main(args: Array[String])
{
// objects of GFG class
var obj = new GFG()
obj.portal()
obj.tutdetails()
4. 191AIC302T-Object Oriented Programming with SCALA
Unit-IV
Department of AI & DSPage 4
}
}
Like Java, In Scala, an abstract class can also contain a constructor and a constructor of an
abstract class is called when an instance of a inherited class is created. As shown in the below
program.
// Scala program to illustrate
// the concept of abstract class
// Abstract class with constructor
// And the constructor contain two arguments
abstract class myauthor(name: String,
topic: String)
{
def details()
}
// GFG class extends abstract class
class GFG(name: String, topic: String) extends
myauthor(name, topic)
{
def details()
{
println("Author name: " + name)
println("Topic name: " + topic)
}
}
object Main
{
// Main method
def main(args: Array[String])
{
// objects of GFG class
var obj = new GFG("Ankita", "Abstract class")
obj.details()
}
}
An abstract class can also contain only non- abstract method. This allows us to create classes that
cannot be instantiated, but can only be inherited. As shown in the below program.
5. 191AIC302T-Object Oriented Programming with SCALA
Unit-IV
Department of AI & DSPage 5
// Scala program to illustrate
// the concept of abstract class
// Abstract class with
// non-abstract method
abstract class myauthor
{
// Non-abstract method
def details()
{
println("Welcome to GeeksforGeeks")
}
}
// GFG class extends abstract class
class GFG extends myauthor{}
object Main
{
// Main method
def main(args: Array[String])
{
// objects of GFG class
var obj = new GFG()
obj.details()
}
}
In Scala, an abstract class can contain final methods (methods that cannot be overridden). For
example, the following program compiles and runs without an error. In Scala, final method is
created using final keyword.
// Scala program to illustrate
// the concept of abstract class
// Abstract class with the final method
abstract class myauthor
{
final def mymethod()
{
println("Final method")
}
}
6. 191AIC302T-Object Oriented Programming with SCALA
Unit-IV
Department of AI & DSPage 6
// GFG class extends abstract class
class GFG extends myauthor{}
object Main
{
// Main method
def main(args: Array[String])
{
// objects of GFG class
var obj = new GFG()
obj.mymethod()
}
}
When to use abstract class in Scala:
An abstract class is useful:
When we want to construct a base class which needs constructor arguments.
When our code will be called from Java code.
Note:Traits are also used to achieve abstraction.
Abstract Class
A class which is declared with abstract keyword is known as abstract class. An abstract
class can have abstract methods and non-abstract methods as well. Abstract class is used
to achieve abstraction. Abstraction is a process in which we hide complex
implementation details and show only functionality to the user.
In scala, we can achieve abstraction by using abstract class and trait. We have discussed
about these in detail here.
Scala Abstract Class Example
In this example, we have created a Bike abstract class. It contains an abstract method. A
class Hero extends it and provides implementation of its run method.
A class that extends an abstract class must provide implementation of its all abstract
methods. You can't create object of an abstract class.
abstract class Bike{
def run()
}
class Hero extends Bike{
7. 191AIC302T-Object Oriented Programming with SCALA
Unit-IV
Department of AI & DSPage 7
def run(){
println("running fine...")
}
}
object MainObject{
def main(args: Array[String]){
var h = new Hero()
h.run()
}
}
Abstract Class Example: Having Constructor, Variables and Abstract Methods
abstract class Bike(a:Int){ // Creating constructor
var b:Int = 20 // Creating variables
var c:Int = 25
def run() // Abstract method
def performance(){ // Non-abstract method
println("Performance awesome")
}
}
class Hero(a:Int) extends Bike(a){
c = 30
def run(){
println("Running fine...")
println("a = "+a)
println("b = "+b)
println("c = "+c)
}
}
object MainObject{
def main(args: Array[String]){
var h = new Hero(10)
h.run()
h.performance()
}
}
Scala Abstract Class Example: Abstract Method is not implemented
In this example, we didn't implement abstract method run(). Compiler reports an error during
compilation of this program. Error message is given below in output section.
8. 191AIC302T-Object Oriented Programming with SCALA
Unit-IV
Department of AI & DSPage 8
abstract class Bike{
def run() // Abstract method
}
class Hero extends Bike{ // Not implemented in this class
def runHero(){
println("Running fine...")
}
}
object MainObject{
def main(args: Array[String]){
var h = new Hero()
h.runHero()
}
}
Trait
A trait is like an interface with a partial implementation. In scala, trait is a collection of
abstract and non-abstract methods. You can create trait that can have all abstract methods
or some abstract and some non-abstract methods.
A variable that is declared either by using val or var keyword in a trait get internally
implemented in the class that implements the trait. Any variable which is declared by
using val or var but not initialized is considered abstract.
Traits are compiled into Java interfaces with corresponding implementation classes that
hold any methods implemented in the traits.
Scala Trait Example
trait Printable{
def print()
}
class A4 extends Printable{
def print(){
println("Hello")
}
}
object MainObject{
def main(args:Array[String]){
var a = new A4()
a.print()
}
9. 191AIC302T-Object Oriented Programming with SCALA
Unit-IV
Department of AI & DSPage 9
}
If a class extends a trait but does not implement the members declared in that trait, it must be
declared abstract. Let's see an example.
Scala Trait Example
trait Printable{
def print()
}
abstract class A4 extends Printable{ // Must declared as abstract class
def printA4(){
println("Hello, this is A4 Sheet")
}
}
Scala Trait Example: Implementing Multiple Traits in a Class
If a class implements multiple traits, it will extend the first trait, class, abstract class. with
keyword is used to extend rest of the traits.
You can achieve multiple inheritances by using trait.
trait Printable{
def print()
}
trait Showable{
def show()
}
class A6 extends Printable with Showable{
def print(){
println("This is printable")
}
def show(){
println("This is showable");
}
}
object MainObject{
def main(args:Array[String]){
var a = new A6()
a.print()
a.show()
}
}
Output:
10. 191AIC302T-Object Oriented Programming with SCALA
Unit-IV
Department of AI & DSPage 10
This is printable
This is showable
Scala Trait having abstract and non-abstract methods
You can also define method in trait as like in abstract class. I.e. you can treat trait as abstract
class also. In scala, trait is almost same as abstract class except that it can't have constructor. You
can't extend multiple abstract classes but can extend multiple traits.
Scala Trait Example
trait Printable{
def print() // Abstract method
def show(){ // Non-abstract method
println("This is show method")
}
}
class A6 extends Printable{
def print(){
println("This is print method")
}
}
object MainObject{
def main(args:Array[String]){
var a = new A6()
a.print()
a.show()
}
}
Trait Mixins
In scala, trait mixins means you can extend any number of traits with a class or abstract
class. You can extend only traits or combination of traits and class or traits and abstract
class.
It is necessary to maintain order of mixins otherwise compiler throws an error.
You can use mixins in scala like this:
Scala Trait Example: Mixins Order Not Maintained
In this example, we have extended a trait and an abstract class. Let's see what happen.
trait Print{
def print()
}
11. 191AIC302T-Object Oriented Programming with SCALA
Unit-IV
Department of AI & DSPage 11
abstract class PrintA4{
def printA4()
}
class A6 extends Print with PrintA4{
def print(){ // Trait print
println("print sheet")
}
def printA4(){ // Abstract class printA4
println("Print A4 Sheet")
}
}
object MainObject{
def main(args:Array[String]){
var a = new A6()
a.print()
a.printA4()
}
}
Output:
error: class PrintA4 needs to be a trait to be mixed in
class A6 extends Print with PrintA4{
^
one error found
The above program throws a compile time error, because we didn't maintain mixins order.
Scala Mixins Order
The right mixins order of trait is that any class or abstract class which you want to extend, first
extend this. All the traits will be extended after this class or abstract class.
Scala Trait Example: Mixins Order Maintained
trait Print{
def print()
}
abstract class PrintA4{
def printA4()
}
class A6 extends PrintA4 with Print{ // First one is abstract class second one is trait
def print(){ // Trait print
println("print sheet")
}
12. 191AIC302T-Object Oriented Programming with SCALA
Unit-IV
Department of AI & DSPage 12
def printA4(){ // Abstract class printA4
println("Print A4 Sheet")
}
}
object MainObject{
def main(args:Array[String]){
var a = new A6()
a.print()
a.printA4()
}
}
Output:
print sheet
Print A4 Sheet
Another Example of Scala Trait
Here, we have used one more approach to extend trait in our program. In this approach, we
extend trait during object creation. Let's see an example.
trait Print{
def print()
}
abstract class PrintA4{
def printA4()
}
class A6 extends PrintA4 {
def print(){ // Trait print
println("print sheet")
}
def printA4(){ // Abstract class printA4
println("Print A4 Sheet")
}
}
object MainObject{
def main(args:Array[String]){
var a = new A6() with Print // You can also extend trait during object creation
a.print()
a.printA4()
}
}
13. 191AIC302T-Object Oriented Programming with SCALA
Unit-IV
Department of AI & DSPage 13
Encapsulation
Encapsulation is one of the fundamental concepts in object-oriented programming. It describes
the idea of bundling data and methods that work on that data within a class.
Let’s consider a class that represents a Sandwich:
import scala.collection.mutable.ArrayBuffer
class Sandwich(bread: Bread, filling: ArrayBuffer[String]) {
private def getFillingsName: String = {
filling.mkString(", ")
}
}
In the class definition, all properties and methods are private and we cannot access them from the
object we created. Doing so will result in a compilation error:
val sandwich = new Sandwich(new Bread("white"), ArrayBuffer("strawberry jam", "chocolate"))
sandwich.bread // error: value bread is not a member of Sandwich
sandwich.filling // error: value filling is not a member of Sandwich
sandwich.getFillingsName // error: method getFillingsName in class Sandwich cannot be
accessed in Sandwich
We can access or manipulate them with public methods:
def getDescription: String = {
s"This is a sandwich with ${bread.name} bread and $getFillingsName filling"
}
def addFilling(extraFilling: String): Unit = {
filling.append(extraFilling)
}
With the defined methods, now we can access and manipulate the private fields and methods of
the class:
val sandwich = new Sandwich(new Bread("sourdough"), ArrayBuffer("chicken"))
sandwich.addFilling("lettuce")
assert(sandwich.getDescription === "This is a sandwich with sourdough bread and chicken,
lettuce filling")
The Inner class means characterizing a class into another. This element empowers the users to
sensibly bunch classes that are just utilized in one place, subsequently this expands the utilization
of encapsulation, and make more creatable and viable code. In Scala, the idea of inner classes is
not the same as Java. Like in Java, the inner class is the member from the external class, yet in
Scala, the internal class is bound to the outer object.
14. 191AIC302T-Object Oriented Programming with SCALA
Unit-IV
Department of AI & DSPage 14
Use case 1:
// Outer class
class prwatech {
// Inner class
class P1
{
var a = 1
def method()
{
for(a<-0 to 2)
{
println("Welcome to Prwatech Data Science");
}
}
}
}
object Main
{
def main(args: Array[String])
{
// Creating object of the outer and
// inner class Here, P1 class is
// bounded with the object of Prwatech class
val obj = new prwatech();
val o = new obj.P1;
o.method();
}
}
Output
Welcome to Prwatech Data Science
Welcome to Prwatech Data Science
Welcome to Prwatech Data Science
Use case 2:
// Class inside Object
class outer_class_Prwatech
{
object inner_object_Prwatech1
{
val a = 0;
def method()
15. 191AIC302T-Object Oriented Programming with SCALA
Unit-IV
Department of AI & DSPage 15
{
for(a <- 1 to 2)
{
println("object inside a class Prwatech")
}
println()
}
}
}
// Object inside Class
object outer_objectPrwatech
{
class innerPrwatech
{
val b = 0;
def method()
{
for(b <- 1 to 2)
{
println("class inside an object Prwatech")
}
}
}
}
object Main
{
// Main method
def main(args: Array[String])
{
// Object inside a class
new outer_class_Prwatech().inner_object_Prwatech1.method;
// Class inside an object
new outer_objectPrwatech.innerPrwatech().method;
}
}
Output
object inside a class Prwatech
object inside a class Prwatech
16. 191AIC302T-Object Oriented Programming with SCALA
Unit-IV
Department of AI & DSPage 16
class inside an object Prwatech
class inside an object Prwatech
Use case 3:
class Prwatech
{
// Inner class
class P1
{
var x = 1
def method()
{
for(x<-0 to 0 )
{
println("Welcome to Prwatech");
}
}
}
}
object Main
{
def main(args: Array[String])
{
val obj = new Prwatech();
val o = new obj.P1;
o.method();
}
}
Packages
Package in Scala is a mechanism to encapsulate a group of classes, sub packages, traits and
package objects. It basically provides namespace to put our code in a different files and
directories. Packages is a easy way to maintain our code which prevents naming conflicts of
members of different packages. Providing access control to members of packages like private,
protected, package specific controlling scope restricts the access of member to other packages,
while the members with no modifier can be used inside any other package with some reference.
Declaration of Package
Packages are declared as a first statement at the top of a Scala file.
Syntax :
package package_name
// Scala classes
17. 191AIC302T-Object Oriented Programming with SCALA
Unit-IV
Department of AI & DSPage 17
// traits
// objects..
Defining a package can be done in different ways:-
Chained methods
package x.y.z
// members of z
or can be used as:-
package x
package y
package z
// member of z
Nesting packages
package x{
// members of x {as required}
package y{
// members of y{as required}
package z{
// members of z{as required}
}
}
}
How Package works
Packages binds together the data in a single file or works as data encapsulation, when a file is
saved it comes under default package or under a package name as specified at the top of the file.
Package names and directory structure are closely related. For example if a package name is
college.student.cse, then there will be 3 directories, college, student and cse. Such that cse is
present in student and student is present in college.
college
+student
+cse
The idea is to make sure that files are easy to locate in directories while using the packages.
Package naming conventions for domain names are in reverse order as i.e.
org.geeksforgeeks.practice, org.geeksforgeeks.contribute.
Adding Members to Packages
We can add any numbers of members in a package such as classes, subclasses, traits, object
containing the main method and sub packages. Unlike the java packages we can add a declared
packages in different scala files i.e. different scala files can be written can for the same package.
Example:
// file named as faculty.scala
18. 191AIC302T-Object Oriented Programming with SCALA
Unit-IV
Department of AI & DSPage 18
package college
class faculty{
def faculymethod(){}
}
// file named as student.scala
// containing the main method
// using the college package name again
package college
class student
{
def studentmethod(){}
}
// Creating object
object Main
{
// Main method
def main(args: Array[String])
{
val stu= new student()
val fac= new faculty()
// faculty class can be accessed while
// in different file but in same package.
}
}
What is actually created in directory structure is as below:-
college
+faculty.scala
+student.scala
Using Packages
Packages can be used by different ways in a program. Import clauses are quite flexible in Scala
than in java. Such as import clauses can be used any where in the program as an independent
statement in the program using the keyword import, Java does not allow that.
// base.scala
// bb directory
package bb
// creating a class
class geek
{
private var id=0
19. 191AIC302T-Object Oriented Programming with SCALA
Unit-IV
Department of AI & DSPage 19
def method()
{
println("welcome to geek class")
println("id="+id)
}
}
Below is the example of Package using import clauses.
// main.scala
// aa directory
package aa
// Creating object
object Main
{
// Main method
def main(args: Array[String])
{
// importing in main method
import bb.geek
// using the member injected using import statement
val obj = new geek()
obj.method();
}
}
Different ways to use import statements
Importing all public members of package.
import college._
//imports all college members students, faculties, houseKeeping etc.
Imports only selected members of a package.
import college.{faculty, houseKeeping}
//member student is not selected hence cannot be used in current file
Imports and rename the members.
import college.{student => stu}
//stu is used in-place of student
Packages and Imports
Scala uses packages to create namespaces which allow you to modularize programs.
Creating a package
Packages are created by declaring one or more package names at the top of a Scala file.
20. 191AIC302T-Object Oriented Programming with SCALA
Unit-IV
Department of AI & DSPage 20
package users
class User
One convention is to name the package the same as the directory containing the Scala
file. However, Scala is agnostic to file layout. The directory structure of an sbt project for
package users might look like this:
- ExampleProject
- build.sbt
- project
- src
- main
- scala
- users
User.scala
UserProfile.scala
UserPreferences.scala
- test
Notice how the users directory is within the scala directory and how there are multiple Scala files
within the package. Each Scala file in the package could have the same package declaration.
The other way to declare packages is by using braces:
package users {
package administrators {
class NormalUser
}
package normalusers {
class NormalUser
}
}
As you can see, this allows for package nesting and provides greater control for scope and
encapsulation.
The package name should be all lower case and if the code is being developed within an
organization which has a website, it should be the following format convention: <top-level-
domain>.<domain-name>.<project-name>. For example, if Google had a project called
SelfDrivingCar, the package name would look like this:
package com.google.selfdrivingcar.camera
class Lens
This could correspond to the following directory structure:
SelfDrivingCar/src/main/scala/com/google/selfdrivingcar/camera/Lens.scala.
21. 191AIC302T-Object Oriented Programming with SCALA
Unit-IV
Department of AI & DSPage 21
Imports
import clauses are for accessing members (classes, traits, functions, etc.) in other packages. An
import clause is not required for accessing members of the same package. Import clauses are
selective:
import users._ // import everything from the users package
import users.User // import the class User
import users.{User, UserPreferences} // Only imports selected members
import users.{UserPreferences => UPrefs} // import and rename for convenience
One way in which Scala is different from Java is that imports can be used anywhere:
def sqrtplus1(x: Int) = {
import scala.math.sqrt
sqrt(x) + 1.0
}
In the event there is a naming conflict and you need to import something from the root of the
project, prefix the package name with _root_:
package accounts
import _root_.users._
Package Objects
Package objects allow us to keep methods, functions, and variables directly aligned with a
package. As a result, all the child members of this package would be able to access them.
For each package, we are only allowed to create one package object where we put the source
code for the package in. According to the Scala convention, we should always call it
package.scala.
package com.baeldung.scala
package object packageimport {
val year = "2020"
trait Motor {
val dieselMessage: String = "I am not environment friendly"
val noDieselMessage: String = "I am environment friendly"
}
}
22. 191AIC302T-Object Oriented Programming with SCALA
Unit-IV
Department of AI & DSPage 22
All members of com.baeldung.scala.package will be able to access year and Motor. In addition,
we won’t have to import package object in any of those members:
object bmwB38 extends Motor {
def run(): Unit =
println(s"I am a bmwB38 ! $noDieselMessage")
}
Access Modifier
Access modifier is used to define accessibility of data and our code to the outside world. You can
apply accessibly to classes, traits, data members, member methods and constructors etc. Scala
provides least accessibility to access to all. You can apply any access modifier to your code
according to your application requirement.
Scala provides only three types of access modifiers, which are given below:
In scala, if you don't mention any access modifier, it is treated as no modifier.
Scala Example: Private Access Modifier
In scala, private access modifier is used to make data accessible only within class in which it is
declared. It is most restricted and keeps your data in limited scope. Private data members does
not inherit into subclasses.
class AccessExample{
private var a:Int = 10
def show(){
println(a)
}
}
object MainObject{
def main(args:Array[String]){
var p = new AccessExample()
p.a = 12
p.show()
23. 191AIC302T-Object Oriented Programming with SCALA
Unit-IV
Department of AI & DSPage 23
}
}
Output:
error: variable a in class AccessExample cannot be accessed in AccessExample
p.a = 12
^
one error found
Scala Example: Protected Access Modifier
Protected access modifier is accessible only within class, sub class and companion object. Data
members declared as protected are inherited in subclass. Let's see an example.
class AccessExample{
protected var a:Int = 10
}
class SubClass extends AccessExample{
def display(){
println("a = "+a)
}
}
object MainObject{
def main(args:Array[String]){
var s = new SubClass()
s.display()
}
}
Output:
a = 10
Scala Example: No-Access-Modifier
In scala, when you don't mention any access modifier, it is treated as no-access-modifier. It is
same as public in java. It is least restricted and can easily accessible from anywhere inside or
outside the package.
class AccessExample{
var a:Int = 10
def show(){
println(" a = "+a)
}
}
object MainObject{
24. 191AIC302T-Object Oriented Programming with SCALA
Unit-IV
Department of AI & DSPage 24
def main(args:Array[String]){
var a = new AccessExample()
a.show()
}
}
There are Three types of access modifiers available in Scala:
1. Private
2. Protected
3. Public
1. Private: When a member is declared as private, we can only use it inside defining class or
through one of its objects.
Example:
// Scala program of private access modifier
class abc
{
private var a:Int = 123
def display()
{
a = 8
println(a)
}
}
object access extends App
{
// class abc is accessible
// because this is in the same enclosing scope
var e = new abc()
e.display()
}
Output:
8
Here we declared a variable ‘a’ private and now it can be accessed only inside it’s defining class
or through classes object.
25. 191AIC302T-Object Oriented Programming with SCALA
Unit-IV
Department of AI & DSPage 25
2. Protected: They can be only accessible from sub classes of the base class in which the
member has been defined.
Example:
// Scala program of protected access modifier
class gfg
{
// declaration of protected member
protected var a:Int = 123
def display()
{
a = 8
println(a)
}
}
// class new1 extends by class gfg
class new1 extends gfg
{
def display1()
{
a = 9
println(a)
}
}
object access extends App
{
// class abc is accessible because this
// is in the same enclosing scope
var e = new gfg()
e.display()
var e1 = new new1()
e1.display1()
}
Output:
8
9
When we extended abc in class new1, protected variable a is now available to be modified cause
new1 is a subclass of class abc.
26. 191AIC302T-Object Oriented Programming with SCALA
Unit-IV
Department of AI & DSPage 26
3. Public: There is no public keyword in Scala. The default access level (when no modifier is
specified) corresponds to Java’s public access level.We can access these anywhere.
// Scala program of protected access modifier
class gfg
{
var a:Int = 123
}
object access extends App
{
var e = new gfg()
e.a = 444
println(e.a)
}
Realtime Example of Encapsulation
Realtime Example 1:
School bag is one of the most real examples of Encapsulation. School bag can keep our books,
pens, etc.
Realtime Example 2:
When you log into your email accounts such as Gmail, Yahoo Mail, or Rediff mail, there is a lot
of internal processes taking place in the backend and you have no control over it.
When you enter the password for logging, they are retrieved in an encrypted form and verified,
and then you are given access to your account.
You do not have control over it that how the password has been verified. Thus, it keeps our
account safe from being misused.
Realtime Example 3:
Suppose you have an account in the bank. If your balance variable is declared as a public
variable in the bank software, your account balance will be known as public, In this case, anyone
can know your account balance. So, would you like it? Obviously No.
So, they declare balance variable as private for making your account safe, so that anyone cannot
see your account balance.
The person who has to see his account balance, will have to access only private members
through methods defined inside that class and this method will ask your account holder name or
user Id, and password for authentication.
27. 191AIC302T-Object Oriented Programming with SCALA
Unit-IV
Department of AI & DSPage 27
Thus, we can achieve security by utilizing the concept of data hiding. This is called
Encapsulation in Java.
How to achieve or implement Encapsulation
There are two important points whereby we can achieve or implement encapsulation in Java
program.
1. Declaring the instance variable of the class as private. so that it cannot be accessed directly by
anyone from outside the class.
2. Provide the public setter and getter methods in the class to set/modify the values of the
variable/fields.
Advantage of Encapsulation
There are following advantages of encapsulation in Java. They are as follows:
1. The encapsulated code is more flexible and easy to change with new requirements.
2. It prevents the other classes to access the private fields.
3. Encapsulation allows modifying implemented code without breaking other code that has
implemented the code.
4. It keeps the data and codes safe from external inheritance. Thus, Encapsulation helps to
achieve security.
5. It improves the maintainability of the application.
6. If you don’t define the setter method in the class then the fields can be made read-only.
7. If you don’t define the getter method in the class then the fields can be made write-only.
Disadvantage of Encapsulation
The main disadvantage of encapsulation in Java is it increases the length of the code and slows
shutdown execution.
Data Hiding
Data hiding in Java is an important principle of object-oriented programming system
(OOPs). It prevents to access data members (variables) directly from outside the class so
that we can achieve security on data. This oops feature is called data hiding in Java.
An outside person could not access our internal data directly or our internal data should
not go out directly. After validation or authentication, the outside person can access our
internal data.
For example, after providing proper username and password, you can able to access your
Gmail inbox information.
How to achieve Data hiding programmatically?
28. 191AIC302T-Object Oriented Programming with SCALA
Unit-IV
Department of AI & DSPage 28
By declaring data members (variables) as private, we can achieve or implement data hiding. If
the variables are declared as private in the class, nobody can access them from outside the class.
The biggest advantage of data hiding is we can achieve security.
Key points:
1. It is highly recommended to declare data members as private in the class.
2. A combination of data hiding and abstraction is nothing but encapsulation.
Encapsulation = Data Hiding + Abstraction
If any component follows data hiding and abstraction, it is called an encapsulated component.
Type Class
A type class is a group of types that satisfy a contract typically defined by a trait. They enable us
to make a function more ad-hoc polymorphic without touching its code. This flexibility is the
biggest win with the type-class pattern.
More formally put:
A type class is a type system construct that supports ad hoc polymorphism. This is achieved by
adding constraints to type variables in parametrically polymorphic types. Such a constraint
typically involves a type class T and a type variable a, and means that a can only be instantiated
to a type whose members support the overloaded operations associated with T
To unpack this definition, we need to identify the different terms used and define them
separately:
Type System – this is a logical system in the compiler that defines the rules for assigning the
property we call type to different programming constructs such as variables, expressions, or
functions. Int, String, and Long are the examples of types we are referring to here
Ad-hoc and Parametric Polymorphism – we covered these in our article about polymorphism in
Scala
Example Problem
Let’s create an extremely arbitrary example to see how we’d define and use type classes. Assume
we want to build a printing mechanism to display a couple of objects in a school information
system.
In the beginning, we have only StudentId, which wraps integer-based student IDs for type safety.
But with time, we expect to add several other data wrappers like StaffId, and even business
domain objects like Score, without touching the original implementation.
29. 191AIC302T-Object Oriented Programming with SCALA
Unit-IV
Department of AI & DSPage 29
The flexibility of the type-class pattern is exactly what we need here. So, let’s define these
objects:
case class StudentId(id: Int)
case class StaffId(id: Int)
case class Score(s: Double)
Since we’ve defined the problem, let’s go ahead and create a type-class to solve it. We’ll try to
reference the formal definition we quoted in the previous section so that it’s easy to map it to
implementation.
Defining Type Class T
In this section, we’ll define the type-class that provides the contract or constraint for our
solution:
trait Printer[A] {
def getString(a: A): String
}
We may find it a bit confusing that what we refer to as the type-class is only the trait, yet the
formal definition seems to imply there’s more. For clarity, let’s think of a type-class as a trait
applied in the context of what we referred to as the type-class constructor pattern. A trait on its
own or used in any other way does not qualify as a type-class.
As we saw earlier in the second sentence in the formal definition: This is achieved by adding
constraints to type variables in parametrically polymorphic types. In our case, the type class
above is the parametrically polymorphic type since we defined it with an unbounded type A,
meaning we can substitute A with any type when extending it.
Defining the Printer
The reason we use type classes is to make programs ad-hoc polymorphic, so let’s define the
function we want to apply it to:
def show[A](a: A)(implicit printer: Printer[A]): String = printer.getString(a)
We call the above function whenever we need to show data objects or wrappers in a human-
friendly manner. But we still haven’t empowered it to print anything since we haven’t defined
any type variables (a from the formal definition) for it.
Also notice that the method has two parameter lists. The first list contains ordinary functional
parameters, albeit abstract ones. The second parameter list has the printer parameter prefixed
with the implicit keyword. This should make sense after reading the article about implicit
classes.
Defining Type Variable a
30. 191AIC302T-Object Oriented Programming with SCALA
Unit-IV
Department of AI & DSPage 30
Again, referring to the formal definition of a type-class, the last sentence reads: …a can only be
instantiated to a type whose members support the overloaded operations associated with T.
In our case, this simply means that our type variable should subtype the type-class Printer or
directly instantiate it so it can overload the getString method. By implication, the type variable
should also be implicit since that is how the show method expects it:
implicit val studentPrinter: Printer[StudentId] = new Printer[StudentId] {
def getString(a: StudentId): String = s"StudentId: ${a.id}"
}
At this stage, we can call show for StudentId types:
it should "print StudentId types" in {
val studentId = StudentId(25)
assertResult(expected = "StudentId: 25")(actual = show(studentId))
}
Extending the Type Class
So far, we have a working type-class implementation. In this section, we’ll see the flexibility of
type classes in action. We noted earlier that the type-class pattern enables us to achieve ad-hoc
polymorphism. This means that, without touching the show method, we can increase the range of
types it can handle.
We achieve a larger range by adding new type variables. In our case, we want to be able to print
StaffId and Score types such that the following test passes:
it should "custom print different types" in {
val studentId = StudentId(25)
val staffId = StaffId(12)
val score = Score(94.2)
assertResult(expected = "StudentId: 25")(actual = show(studentId))
assertResult(expected = "StaffId: 12")(actual = show(staffId))
assertResult(expected = "Score: 94.2%")(actual = show(score))
}
Now let’s define the necessary type variables. Typically, we group them in a companion object
(some people call the type-class pattern implicit objects for this reason):
object Printer {
implicit val studentPrinter: Printer[StudentId] = new Printer[StudentId] {
def getString(a: StudentId): String = s"StudentId: ${a.id}"
}
31. 191AIC302T-Object Oriented Programming with SCALA
Unit-IV
Department of AI & DSPage 31
implicit val staffPrinter: Printer[StaffId] = new Printer[StaffId] {
def getString(a: StaffId): String = s"StaffId: ${a.id}"
}
implicit val scorePrinter: Printer[Score] = new Printer[Score] {
def getString(a: Score): String = s"Score: ${a.s}%"
}
}
That’s it. Any time we need to support new object types, we only need to add a type variable in
the Printer companion object.
Type Conversions
Types in Scala
Type also know as data type tells the compiler about the type of data that is used by the
programmer. For example, if we initialize a value or variable as an integer the compiler will free
up 4 bytes of memory space and it will hold 32 bit signed integer type.
Type Casting in Scala
Type casting is the process of converting that type from one to another. For example, We
can convert int type to float type in Scala.
But this conversion cannot be two ways and is derived from certain types i.e. the
conversion from one type to another is possible and the reverse is not. This is due to the
different sizes of data type and conversion from type of size greater to a smaller one
might lead to data losses.
The following conversions are valid:
Character -> Integer
Short -> Integer
Byte -> Integer
Integer -> Long
Long -> Float
32. 191AIC302T-Object Oriented Programming with SCALA
Unit-IV
Department of AI & DSPage 32
Float -> Double
But, the reverse conversions can be done but are invalid due to data losses. For example,
Double -> Float is invalid.
Types of Type Casting
There can be two types of typecasting as all programming languages have,
Implicit type casting
Explicit type casting
1) Implicit type casting
In implicit type Casting of type in Scala the compiler itself cast the type of the
value/variable. This conversion can be lossy i.e. in some cases data may be lost. For
example, in the case of division of two int values which can return a non-integer (float/
double) value, the result will be of integer type which can lead to data loss.
Scala program to demonstrate example of implicit type casting
object myObject {
def main(args: Array[String]) {
val a : Int = 3421
println("a has value: " + a + " and its type is: " + a.getClass)
val c = a / 4 // result is 855.25 but will be converted to Int
println("The value of a/4: " + c + " and its type is: " + c.getClass)
}
}
Output:
a has value: 3421 and its type is: int
The value of a/4: 855 and its type is: int
In the above code, we have created a value a of type integer with value 3421, then we
have divided a by 4 and stored the result in value c. This division leaves to a decimal
point value but due to implicit type conversion, it is stored in integer type which two
losses.
This problem can be solved by using explicit conversion to avoid data loss but in some
cases that may lead to excessive memory wastage.
2) Explicit Type Conversion
The explicit type conversion is user-defined type conversion i.e. the user will decide that
final data type of the value/variable.
The type conversion of value types can be done directly but for reference types, the
conversion required asInstanceOf method.
33. 191AIC302T-Object Oriented Programming with SCALA
Unit-IV
Department of AI & DSPage 33
As the asInstanceOf method is a concrete method of Any Class, it can be used for type
conversion of AnyVal object and AnyRef objects too (object conversion).
Let's see the examples of both the methods in action:
Example 1: Explicit type conversion
object myObject {
def main(args: Array[String]) {
// Type conversion from Short to Long
val a : Short = 3421
println("a has value: " + a + " and its type is: " + a.getClass)
val b : Long = a // converting type from short to long
println("Type casting from Short to Long")
println("b has value: " + b + " and its type is: " + b.getClass)
// Type conversion from Char to Float
val ch : Char = 'S'
println("nch has value: " + ch + " and its type is: " + ch.getClass)
val fl : Float = ch // converting type from Char to Float
println("Type casting from Character to Float")
println("fl has value: " + fl + " and its type is: " + fl.getClass)
}
}
Output:
a has value: 3421 and its type is: short
Type casting from Short to Long
b has value: 3421 and its type is: long
ch has value: S and its type is: char
Type casting from Character to Float
fl has value: 83.0 and its type is: float
In the above code, we have done two types of conversions. One from short to long and
other from char to float.
For short to long, we have created variable a of type short that stores a value 3421, and
then we have created another variable b of type long which is initialized with the value of
short.
For char to float, we have created variable ch of type char that stores a value S, and then
we have created another variable fl of type float which is initialized with the value of
char. This will have the float type of ASCII value of 'S'.
Example 2: Explicit conversion using asInstanceOf method
34. 191AIC302T-Object Oriented Programming with SCALA
Unit-IV
Department of AI & DSPage 34
object myObject {
def main(args: Array[String]) {
// Type conversion from Short to Flaot
val a : Short = 3421
println("a has value: " + a + " and its type is: " + a.getClass)
val b = a.asInstanceOf[Double] // converting type from short to long
println("Type casting from Short to Double")
println("b has value: " + b + " and its type is: " + b.getClass)
// Type conversion from Char to Int
val ch : Char = 'S'
println("nch has value: " + ch + " and its type is: " + ch.getClass)
val intVal = ch.asInstanceOf[Int] // converting type from Char to Int
println("Type casting from Character to Int")
println("intVal has value: " + intVal + " and its type is: " + intVal.getClass)
}
}
Output:
a has value: 3421 and its type is: short
Type casting from Short to Double
b has value: 3421.0 and its type is: double
ch has value: S and its type is: char
Type casting from Character to Int
intVal has value: 83 and its type is: int
In the above code, we have done two types of conversions. One from short to Double and
other from char to int.
For short to Double, we have created variable a of type short that stores a value 3421, and
then we have created another variable b of type Double which is initialized using the
asInstanceOf method for conversion of type.
For char to float, we have created variable ch of type char that stores a value S, and then
we have created another variable intVal of type int which is initialized with the value of
char using the asInstanceOf method. The data in the intVal is the ASCII value for the
character.