
- Scala - Home
- Scala - Overview
- Scala - Features
- Scala - Environment Setup
- Scala - Build Tool (SBT)
- Scala - REPL
- Scala - Dot & Dotty
- Scala - Basic Syntax
- Scala - Hello World Program
- Scala - Identifiers
- Scala - Keywords
- Scala - Comments
- Scala - Code Blocks
- Scala - Semicolon
- Scala - Constructs
- Scala - Expressions
- Scala - Input and Output
- Scala - Optional Braces
- Scala - Underscore (_)
- Data Types and Variables
- Scala - Data Types
- Scala - Type Bounds
- Scala - Context Bound
- Scala - Variances
- Scala - Type Hierarchy
- Scala - Variables
- Scala - Variable Scopes
- Scala - Literals
- Scala - Numeric Types
- Scala - Boolean Types
- Scala - Char Type
- Scala - Unit Types
- Scala - Strings
- Scala - Arrays
- Scala - Null Type
- Scala - Nothing
- Scala - Any Type
- Scala - AnyRef Type
- Scala - Unified Types
- Scala - Dates and Times
- Scala - Ranges
- Scala - Multidimensional Arrays
- Scala - WrappedArray
- Scala - StringBuilder
- Scala - String Interpolation
- Scala - StringContext
- Scala - Type Casting
- Scala var vs val
- Scala Operators
- Scala - Operators
- Scala - Rules for Operators
- Scala - Arithmetic Operators
- Scala - Relational Operators
- Scala - Logical Operators
- Scala - Bitwise Operators
- Scala - Assignment Operators
- Scala - Operators Precedence
- Scala - Symbolic Operators
- Scala - Range Operator
- Scala - String Concatenation Operator
- Scala Conditional Statements
- Scala - IF ELSE
- Scala - IF-ELSE-IF-ELSE Statement
- Scala - Nested IF-ELSE Statement
- Scala Loop Statements
- Scala - Loop Statements
- Scala - while Loop
- Scala - do-while Loop
- Scala - Nested Loops
- Scala - for Loop
- Scala - break Statement
- Scala - yield Keyword
- Scala Classes & Objects
- Scala - Classes & Objects
- Scala - Constructors
- Scala - Auxiliary Constructor
- Scala - Primary Constructor
- Scala - This Keyword
- Scala - Nested Classes
- Scala - Getters and Setters
- Scala - Object Private Fields
- Scala - Singleton Object
- Scala - Companion Objects
- Scala - Creating Executable Programs
- Scala - Stateful Object
- Scala - Enumerations
- Scala - Polymorphism
- Scala - Access Modifiers
- Scala - Apply Method
- Scala - Update Methods
- Scala - UnapplySeq Method
- Scala - Inheritance
- Scala - Extending a Class
- Scala - Method Overloading
- Scala - Method Overriding
- Scala - Generic Classes
- Scala - Generic Functions
- Scala - Superclass Construction
- Scala Methods & Functions
- Scala - Functions
- Scala - Main Methods
- Scala - Functions Call-by-Name
- Scala - Functions with Named Arguments
- Scala - Function with Variable Arguments
- Scala - Recursion Functions
- Scala - Default Parameter Values
- Scala - Functions without Parameters
- Scala - Implicit Parameters
- Scala - Higher-Order Functions
- Scala - Nested Functions
- Scala - Extension Methods
- Scala - Anonymous Functions
- Partially Applied Functions
- Scala - Lazy Val
- Scala - Pure Function
- Scala - Currying Functions
- Scala - Control Abstractions
- Scala - Corecursion
- Scala - Unfold
- Scala - Tail Recursion
- Scala - Infinite Sequences
- Scala - Dynamic Invocation
- Scala - Lambda Expressions
- Scala Collections
- Scala - Collections
- Mutable and Immutable Collections
- Scala - Lists
- Scala - Sets
- Scala - Maps
- Scala - TreeMap
- Scala - SortedMap
- Scala - Tuples
- Scala - Iterators
- Scala - Options
- Scala - Infinite Streams
- Scala - Parallel Collections
- Scala - Algebraic Data Types
- Scala Pattern Matching
- Scala - Pattern Matching
- Scala - Type Patterns
- Scala - Exception Handling
- Scala - Extractors
- Scala - Regular Expressions
- Scala Files I/O
- Scala - Files I/O
- Scala Advanced Concepts
- Scala - Closures
- Scala - Futures
- Scala - Promises
- Scala - Traits
- Scala - Trait Mixins
- Scala - Layered Traits
- Scala - Trait Linearization
- Scala - Sealed Traits
- Scala - Transparent Traits
- Scala - Literal Type Arithmetic
- Scala - Inline keyword
- Scala - Def, Var & Val
- Scala - Dropped Features
- Scala - BDD Testing
Scala - Traits
Scala Traits
A trait encapsulates method and field definitions, which can then be reused by mixing them into classes. Unlike class inheritance, in which each class must inherit from just one superclass, a class can mix in any number of traits.
Why Scala Traits are Used?
Traits are used to define object types by specifying the signature of the supported methods. Scala also allows traits to be partially implemented but traits may not have constructor parameters.
Defining a Scala Trait
A trait definition looks just like a class definition except that it uses the keyword trait. The following is the basic example syntax of trait.
Syntax
trait Equal { def isEqual(x: Any): Boolean def isNotEqual(x: Any): Boolean = !isEqual(x) }
This trait consists of two methods isEqual and isNotEqual. Here, we have not given any implementation for isEqual where as another method has its implementation. Child classes extending a trait can give implementation for the un-implemented methods. So a trait is very similar to what we have abstract classes in Java.
Example of Scala Trait
Let us assume an example of trait Equal contain two methods isEqual() and isNotEqual(). The trait Equal contain one implemented method that is isEqual() so when user defined class Point extends the trait Equal, implementation to isEqual() method in Point class should be provided.
Here it is required to know two important method of Scala, which are used in the following example.
obj.isInstanceOf [Point] To check Type of obj and Point are same are not.
obj.asInstanceOf [Point] means exact casting by taking the object obj type and returns the same obj as Point type.
Try the following example program to implement traits.
trait Equal { def isEqual(x: Any): Boolean def isNotEqual(x: Any): Boolean = !isEqual(x) } class Point(xc: Int, yc: Int) extends Equal { var x: Int = xc var y: Int = yc def isEqual(obj: Any) = obj.isInstanceOf[Point] && obj.asInstanceOf[Point].x == y } object Demo { def main(args: Array[String]) { val p1 = new Point(2, 3) val p2 = new Point(2, 4) val p3 = new Point(3, 3) println(p1.isNotEqual(p2)) println(p1.isNotEqual(p3)) println(p1.isNotEqual(2)) } }
Save the above program in Demo.scala. The following commands are used to compile and execute this program.
Command
\>scalac Demo.scala \>scala Demo
Output
true false true
Value classes and Universal Traits
Value classes are new mechanism in Scala to avoid allocating runtime objects. It contains a primary constructor with exactly one val parameter. It contains only methods (def) not allowed var, val, nested classes, traits, or objects. Value class cannot be extended by another class. This can be possible by extending your value class with AnyVal. The typesafety of custom datatypes without the runtime overhead.
Example
Let us take an examples of value classes Weight, Height, Email, Age, etc. For all these examples it is not required to allocate memory in the application.
A value class not allowed to extend traits. To permit value classes to extend traits, universal traits are introduced which extends for Any.
trait Printable extends Any { def print(): Unit = println(this) } class Wrapper(val underlying: Int) extends AnyVal with Printable object Demo { def main(args: Array[String]) { val w = new Wrapper(3) w.print() // actually requires instantiating a Wrapper instance } }
Save the above program in Demo.scala. The following commands are used to compile and execute this program.
Command
\>scalac Demo.scala \>scala Demo
It will give you the hash code of Wrapper class.
Wrapper@13
When to Use Traits?
There is no firm rule, but here are few guidelines to consider −
If the behavior will not be reused, then make it a concrete class. It is not reusable behavior after all.
If it might be reused in multiple, unrelated classes, make it a trait. Only traits can be mixed into different parts of the class hierarchy.
If you want to inherit from it in Java code, use an abstract class.
If you plan to distribute it in compiled form, and you expect outside groups to write classes inheriting from it, you might lean towards using an abstract class.
If efficiency is very important, lean towards using a class.
Extending Traits and Mixing Them In
Scala traits provide a powerful way to compose behavior from multiple sources. You can mix in multiple traits into a single class to combine their behavior. Let's extend the previous example to include additional traits and see how they interact.
Example
trait Printable extends Any { def print(): Unit = println(this) } trait Movable { def move(dx: Int, dy: Int): Unit } trait Equal { def isEqual(obj: Any): Boolean def isNotEqual(obj: Any): Boolean = !isEqual(obj) } class Point(xc: Int, yc: Int) extends Equal with Printable with Movable { var x: Int = xc var y: Int = yc def isEqual(obj: Any): Boolean = obj.isInstanceOf[Point] && obj.asInstanceOf[Point].x == x && obj.asInstanceOf[Point].y == y def move(dx: Int, dy: Int): Unit = { x += dx y += dy } override def print(): Unit = println(s"Point($x, $y)") } object Demo { def main(args: Array[String]): Unit = { val p1 = new Point(2, 3) val p2 = new Point(2, 4) val p3 = new Point(2, 3) println(p1.isNotEqual(p2)) println(p1.isNotEqual(p3)) println(p1.isNotEqual(2)) p1.print() p1.move(1, 1) p1.print() } }
Save the above program in Demo.scala. The following commands are used to compile and execute this program.
Command
\>scalac Demo.scala \>scala Demo
The output will be −
true false true Point(2, 3) Point(3, 4)
In this extended example, the Point class now mixes in the Printable and Movable traits in addition to the Equal trait. This demonstrates how multiple traits can be combined to provide a rich set of behaviors to a class. The Point class implements the move method from the Movable trait and overrides the print method from the Printable trait to provide a custom string representation.
Key Aspects of Scala Traits
- Trait Composition − Traits provide a powerful way to compose behavior in Scala. You can mix in multiple traits into a single class, combining their functionalities seamlessly.
- Trait Reuse − Use traits to define reusable behaviors that can be mixed into multiple, unrelated classes. This promotes code reuse and modularity.
- Efficiency Considerations − While traits are flexible, be mindful of the potential performance implications of using multiple traits, especially in performance-critical applications.
- Partial Implementation − Traits can have partially implemented methods, allowing you to provide default behavior that can be overridden by classes mixing in the trait.
- Universal Traits − Use universal traits to allow value classes to extend traits, enabling the combination of value classes' efficiency with traits' flexibility