Scala Tutorial
Basics
Control Statements
OOP Concepts
Parameterized - Type
Exceptions
Scala Annotation
Methods
String
Scala Packages
Scala Trait
Collections
Scala Options
Miscellaneous Topics
Scala has a rich type system, and understanding the type hierarchy is crucial for effective programming in the language. At the top of the hierarchy, there are two universal types: Any
and Nothing
. Let's explore the Scala type hierarchy in detail:
Any:
Any
class, either directly or indirectly.equals
, hashCode
, and toString
that are universal to all Scala objects.AnyVal:
AnyVal
include: Double
, Float
, Long
, Int
, Char
, Short
, Byte
, Unit
, and Boolean
.Int
in Scala is a subtype of AnyVal
and is usually represented as a primitive int
in the JVM at runtime.AnyRef:
AnyRef
corresponds to java.lang.Object
.AnyRef
. This includes all user-defined classes, collections, and more.List[Int]
or a custom class like class Person
would be a subtype of AnyRef
.Nothing:
Nothing
. It's used to indicate abnormal termination, such as throwing an exception.Nothing
is a subtype of all types, it can be used as a type parameter to indicate certain kinds of behavior. For example, the Nil
object, which represents an empty list, is of type List[Nothing]
.Null:
AnyRef
types (i.e., all reference types) but not of value types.Null
has a single value: null
. This is the default value for all reference types and corresponds to Java's null
reference.Specialized Types:
@specialized
annotation.Abstract Types and Path-Dependent Types:
Type Bounds:
<:
) or lower (>:
) bounds, restricting the types that can be used as type arguments.In summary, understanding the Scala type hierarchy, especially the distinctions between AnyVal
, AnyRef
, Nothing
, and Null
, can help when navigating the rich type system of the language. The ability to use and understand advanced features like path-dependent types, abstract types, and type bounds further enhances the power and flexibility of Scala's type system.
Traits in Scala are similar to interfaces in other languages, providing a way to declare and share fields and methods among classes. They form a hierarchy when extended.
trait Printable { def print(): Unit } class MyClass extends Printable { def print(): Unit = println("Printing...") }
Object hierarchy in Scala is rooted at Any
, and all non-primitive types inherit from it. There are two main branches: AnyVal
for value types and AnyRef
for reference types.
class MyClass val instance: AnyRef = new MyClass val value: AnyVal = 42
AnyVal
represents value types with no identity, and AnyRef
represents reference types with identity.
val intValue: AnyVal = 42 val refValue: AnyRef = "Hello" // Use pattern matching to distinguish intValue match { case x: AnyVal => println("It's a value type") case _ => println("It's not a value type") }
Covariance allows subtyping relationships to be preserved, while contravariance reverses the relationships.
class Container[+A](val value: A) val intContainer: Container[Int] = new Container(42) val anyContainer: Container[Any] = intContainer
Type bounds specify constraints on type parameters, and variance defines how subtyping relationships are inherited.
class Box[A](val value: A) def printValue(box: Box[_ <: AnyVal]): Unit = { println(box.value) } val intBox = new Box(42) val stringBox = new Box("Hello") printValue(intBox) // printValue(stringBox) // Error, as it requires AnyVal
Scala introduces special types like Nothing
and Null
at the bottom of the type hierarchy.
def error(message: String): Nothing = throw new RuntimeException(message) val nullValue: Null = null
Implicits are powerful in Scala and are used for type conversions, parameter injection, etc.
implicit def intToString(value: Int): String = value.toString def printString(str: String): Unit = println(str) val intValue: Int = 42 printString(intValue) // Implicit conversion from Int to String