Topic | Type Inference, Type Ascription and Type Aliasing In scala |
---|---|
Git sample | TypeAscriptionInferenceTest.scala & TypeAliasTest.scala |
- Scala has Type Inference, which means that we can skip telling the type of something in the source code
/* A dummy trait */
trait Thing
/* Instance of new trait is created */
def getThing = new Thing { }
/* Without Type Ascription, the type is infered to be 'Thing' */
val infered = getThing
/* With Type Ascription */
val thing: Thing = getThing
-
Leaving out the Type Ascription is OK. That’s a good idea, in order to make the code more self-documenting
-
Early Member Definitions solve issues that occur when a
trait
defines an abstract value
/* Trait with some property and definitions */
trait TraitWithProperty {
/* Abstract value */
val name: String
override val toString = "TraitWithProperty(" + name + ")"
}
/* 'traitWithProperty' is a subclass of 'TraitWithProperty' now */
val traitWithProperty = new TraitWithProperty { override val name = "DOMAIN" }
/* Implicit call to 'toString' will print TraitWithProperty(null). Why value of property 'name' is 'null'? */
/* When the toString method looks for the value of name, it hasn’t been initialized yet, so it finds the value 'null' */
println(traitWithProperty)
/* Another implemetation to avoid initialization issue */
/* Before the TraitWithProperty trait, there is an anonymous block containing the early member definition */
class AnotherTraitWithProperty extends { val name = "HI" } with TraitWithProperty
/* Now anonymous block (early member definitions) will provide preinitialized value of name property and toString will print 'TraitWithProperty(HI)' */
/* Early member definitions solve issues that occur when a trait defines an abstract value */
println(new AnotherTraitWithProperty)
- How to pass an object into a method. Just saying obj: ExampleObj won’t work because that’s already referring to the instance, so there’s a member called type which should be used in such cases
object ExampleObj
def takeAnObject(obj: ExampleObj.type) = {}
takeAnObject(ExampleObj)
- Type Alias will make the code more readable. It makes sense for the future reader of this class. See the below example
/* Two different types */
type User = String
type Age = Int
/* Call goes to Predef.immutable.Map[A, B]. which creates a map[String => Int] */
var data: Map[User, Age] = Map.empty
- Type Aliases are like Abstract Type Members. Allowing to define templates but instead of using the class
Clazz[A]
syntax, name them inside the class. Java folks will use generics in similar fashion e.g. SimplestContainer
/* In Java : SimplestContainer<A> */
trait SimplestContainer {
/* Abstract Type Member */
type A
def value: A
}
/* 'SimplestContainer' instance can be created without implementing the type member 'A' */
/* Type of 'A' is actualy just a shorthand for type A >: Nothing <: Any, which means 'Anything' */
/* Valid, but 'A' is 'Anything' */
val someObjectOfSimplestContainer = new SimplestContainer
/* An object implementing 'SimplestContainer' and providing type information of 'A' */
object IntContainer extends SimplestContainer {
type A = Int
def value = 42
}
- Type Aliasing can also help in applying constraints. For example you want a container that can only store anything that is of a Number instance
trait OnlyNumbersContainer {
/* Abstract Type Member of rigid type 'Number' */
type A <: Number
def value: A
}
/* Bellow won't compile */
val numberVal = new OnlyNumbersContainer {
/* error: type mismatch; found: String(""); required: this.A */
def value = ""
}