
- 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 - Generic Classes
This chapter takes you through the concept of generic classes in Scala programming. You can write flexible and reusable code using parameterizing types. So you can create classes, methods, and traits that work with any data type.
Generic Classes
Generic classes are classes that take a type parameter. You can create classes that can operate on objects of various types with compile-time type safety.
A generic class is defined with one or more type parameters. These type parameters are used within square brackets after the class name.
Syntax
The syntax of the generic class is -
class ClassName[T] { // class body using type T }
Example
The following example shows a generic class in Scala programming -
class Container[T](value: T) { def getValue: T = value } object Demo { def main(args: Array[String]): Unit = { val intContainer = new Container(42) val stringContainer = new Container("Hello, Scala!") println(intContainer.getValue) // 42 println(stringContainer.getValue) // Hello, Scala! } }
Save the above program in Demo.scala. Use the following commands to compile and execute this program.
Command
> scalac Demo.scala > scala Demo
Output
42 Hello, Scala!
In the example, Container class is defined as a generic class with a type parameter T. Demo object creates instances of Container with different types (Int and String). It accesses the stored values using the getValue method.
Generic Methods
Generic method is a method that takes type parameters. These methods can be defined within generic and non-generic classes.
Syntax
The syntax of the generic method is -
def methodName[T](param: T): ReturnType = { // method body using type T }
Example
The following example shows a generic method in Scala programming -
object Demo { def printValue[T](value: T): Unit = { println(s"Value: $value") } def main(args: Array[String]): Unit = { printValue(42) // Output: Value: 123 printValue("Hello, Scala!") // Output: Value: Hello, Scala! printValue(3.14) // Output: Value: 3.14 } }
Save the above program in Demo.scala. Use the following commands to compile and execute this program.
Command
> scalac Demo.scala > scala Demo
Output
Value: 42 Value: Hello, Scala! Value: 3.14
In the example, the printValue method is defined as a generic method with type parameter T. The method takes a value of type T and prints it. Demo object calls the printValue method with different types (Int, String, and Double).
Multiple Type Parameters
Both Generic classes and methods can have multiple type parameters. So there can be more complex and flexible type definitions.
Syntax
The syntax of multiple type parameter is -
class ClassName[T, U] { // class body using types T and U }
Example
The following example shows a generic class with multiple type parameters in Scala -
class Pair[T, U](val first: T, val second: U) { def getFirst: T = first def getSecond: U = second } object Demo { def main(args: Array[String]): Unit = { val intStringPair = new Pair[Int, String](42, "Scala") val stringDoublePair = new Pair[String, Double]("Pi", 3.14) println(intStringPair.getFirst) // 42 println(intStringPair.getSecond) // Scala println(stringDoublePair.getFirst) // Pi println(stringDoublePair.getSecond) // 3.14 } }
Save the above program in Demo.scala. Use the following commands to compile and execute this program.
Command
> scalac Demo.scala > scala Demo
Output
42 Scala Pi 3.14
In the example, the Pair class is defined as a generic class with two type parameters T and U. The class has a constructor that takes two values of types T and U. It also has two methods: getFirst and getSecond that return the respective values. Demo object creates instances of the Pair class with different types (Int, String, and Double). Then it calls the getFirst and getSecond methods.
Bounded Type Parameters
Bounded type parameters restrict the types that can be used as arguments for a type parameter. Upper bounds and lower bounds are used to enforce these restrictions.
Syntax
The syntax of bounded type parameter is -
def methodName[T <: UpperBound](param: T): ReturnType = { // Method definition } def methodName[T >: LowerBound](param: T): ReturnType = { // Method definition }
Example
The following example shows the use of bounded type parameters in Scala programming -
class Upper class Sub extends Upper class Container[T <: Upper](value: T) { def getValue: T = value } object Demo { def main(args: Array[String]): Unit = { val upperContainer = new Container[Upper](new Upper) val subContainer = new Container[Sub](new Sub) println(upperContainer.getValue) println(subContainer.getValue) } }
Save the above program in Demo.scala. Use the following commands to compile and execute this program.
Command
> scalac Demo.scala > scala Demo
Output
Upper@<hashcode> Sub@<hashcode>
In the example, Container class uses an upper-bounded type parameter T <: Upper. It restricts the type parameter to be Upper and its subclasses. Demo object creates instances of Container with Upper and Sub types and how to access the stored values.
Covariant and Contravariant Type Parameters
Covariance and contravariance control how subtyping between complex types relates to subtyping between their component types. When a type parameter is covariant, you can replace the generic class with a subclass. When a type parameter is contravariant, you can replace the generic class with a superclass.
Syntax
The syntax of covariance and contravariance type parameters is -
class Covariant[+T] { // class body using type T } class Contravariant[-T] { // class body using type T }
Example
The following example shows covariant and contravariant type parameters in Scala programming -
class Animal class Dog extends Animal class Covariant[+T](val value: T) class Contravariant[-T](value: T) object Demo { def main(args: Array[String]): Unit = { val covariant: Covariant[Animal] = new Covariant[Dog](new Dog) val contravariant: Contravariant[Dog] = new Contravariant[Animal](new Animal) println(covariant.value) } }
Save the above program in Demo.scala. Use the following commands to compile and execute this program.
Command
> scalac Demo.scala > scala Demo
Output
Dog@<hashcode>
In the example, the Covariant class uses a covariant type parameter +T. So you can use a Covariant[Animal] to hold a Covariant[Dog]. The Contravariant class uses a contravariant type parameter -T. So, you can use a Contravariant[Dog] to hold a Contravariant[Animal]. Demo object shows how these relationships work.
Generic Classes Summary
- You can define classes with type parameters. It provides flexibility and type safety.
- Generic methods have their own type parameters and can be defined within generic or non-generic classes.
- You can also define multiple type parameters for complex and flexible type definitions.
- Bounded type parameters restrict the types. It can be used as arguments for type constraints.
- Covariant and contravariant type parameters control subtyping relationships between complex types and their component types.