
- 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 - Methods
- Scala - Functions
- Scala - Methods vs 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 - Polymorphic Functions
- 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 - NumericRange
- Scala - Infinite Streams
- Scala - Parallel Collections
- Scala Advanced Types
- Scala - Union Types
- Scala - Intersection Types
- Scala - Type Aliases
- Scala - Structural Types
- Scala - Match Expression
- Scala - Singleton Type Operator
- Scala - Abstract Types
- Scala - Dependent Types
- Scala - Abstract Type Bounds
- Scala - Higher-Kinded Types
- Scala - Opaque Type Alias
- Scala - Path-Dependent Types
- Scala - Type Lambdas
- Scala - Type Inference
- Scala - Algebraic Data Types
- Scala Pattern Matching
- Scala - Pattern Matching
- Scala - Guards
- Scala - Variables in Patterns
- Scala - Type Patterns
- Scala - The Matchable Trait
- Scala - Matching Arrays
- Scala - Matching Lists
- Scala - Matching Tuples
- Scala - Exception Handling
- Scala - Extractors
- Scala - Pattern Bindings
- Scala - Regular Expressions
- Scala - Case Classes
- Scala - Partial Functions
- Scala - Packaging and Imports
- Scala - Implicit Imports
- Scala - Export Clauses
- Scala - Nested Packages
- Scala - Chained Packages
- Scala - Package Objects
- Scala Files I/O
- Scala - Files I/O
- Scala - Writing Files
- Scala - Listing Files
- Scala - Deleting Directories
- Scala - Check File Exists
- 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 - Process Management
- Scala - Scaladoc
- Scala - Literal Type Arithmetic
- Scala - Inline keyword
- Scala - Def, Var & Val
- Scala - Dropped Features
- Scala Unit Testing
- Scala - Unit Testing
- Scala - uTest
- Scala - MUnit
- Scala - ScalaTest Runner
- Scala - ScalaMock
- Scala - JUnit
- Scala - Mocking
- Scala - BDD Testing
Scala - Abstract Types
Abstract types are used to define generic interfaces and abstract data types. So, you can create reusable and modular code structures. These types are defined within traits and abstract classes and are refined by concrete implementations.
Abstract types in Scala are types declared within traits (or abstract classes) without specifying their concrete implementations. Since there will be a high degree of abstraction and flexibility. So, you can define interfaces that can be concretely implemented later.
Defining Abstract Types
You can declare Abstract types within traits (or abstract classes). For example -
trait Container { type A def value: A }
Here, A is an abstract type member of the Container trait. So concrete implementations of Container will need to specify what A is.
Refining Abstract Types
You must define the abstract type for concrete implementations -
class IntContainer extends Container { type A = Int def value: Int = 42 } class StringContainer extends Container { type A = String def value: String = "Hello" }
Here, IntContainer and StringContainer refine the abstract type A to Int and String, respectively. These provide concrete implementations for the value method.
Abstract Classes with Abstract Types
You can also use abstract types in abstract classes. For example -
abstract class Animal { type Sound def makeSound: Sound } class Dog extends Animal { type Sound = String def makeSound: String = "Woof!" } class Cat extends Animal { type Sound = String def makeSound: String = "Meow!" }
Here, Animal defines an abstract type Sound. It is refined in the subclasses Dog and Cat to String.
Abstract Types vs. Type Parameters
Both abstract types and type parameters can have similar goals. But these are some different advantages -
- Scope - You can define abstract types within the scope of a trait (or class) for better encapsulation.
- Inheritance - You can refine abstract types in subclasses in complex inheritance hierarchies.
- Type Aliases - You can use abstract types as type aliases for type definitions within a scope.
Example Comparison
Using type parameters -
trait Container[A] { def value: A } class IntContainer extends Container[Int] { def value: Int = 42 } class StringContainer extends Container[String] { def value: String = "Hello" }
Using abstract types -
trait Container { type A def value: A } class IntContainer extends Container { type A = Int def value: Int = 42 } class StringContainer extends Container { type A = String def value: String = "Hello" }
Both approaches achieve similar results. But abstract types can provide clearer and more concise code in certain contexts.
Type Bounds
You can have upper and lower bounds in Abstract types. It is similar to type parameters for more precise type constraints. For example -
trait BoundedContainer { type A <: Number def value: A } class IntContainer extends BoundedContainer { type A = Integer def value: Integer = 42 }
Here, A is bounded to be a subtype of Number for any concrete implementation of BoundedContainer. It must refine A to a subtype of Number.
Practical Example: Collections
You can use abstract types in the collection library of Scala. Following is the example which shows you how to simplified version of a generic collection -
trait MyCollection { type Elem def add(elem: Elem): MyCollection def head: Elem } class IntCollection extends MyCollection { type Elem = Int private var elements: List[Int] = Nil def add(elem: Int): MyCollection = { elements = elem :: elements this } def head: Int = elements.head } class StringCollection extends MyCollection { type Elem = String private var elements: List[String] = Nil def add(elem: String): MyCollection = { elements = elem :: elements this } def head: String = elements.head }
Here, MyCollection is a trait with an abstract type Elem. The concrete implementations IntCollection and StringCollection define Elem as Int and String, respectively. These provide specific implementations for the methods.
Abstract Types Summary
- Abstract types provide a high level of flexibility for concrete implementations to refine abstract type members.
- You can define abstract types within traits and abstract classes. It can create generic and reusable interfaces.
- You can have type bounds in abstract types for precise type constraints and type safety.
- You can use abstract types in the collection library to provide generic and type-safe collections.