Aka type parameters, parametric polymorphism
We know has to write a specific type. Analogous to method parameters
- define them on a type (trait, class) or a method
- Type is not known at the point of definition (can be any specific type, but we don’t know what that type is), but must be known (concrete) at the point of use (i.e. when using a concrete instance)
- Declaration and use of type parameters
- Declare like a method parameter: declare a name for types:
[A]
for method parameters:(a: A)
- Use of a type parameter: refer to the parameter type parameter
A
or method parameter:a
- Declare like a method parameter: declare a name for types:
- Scope: declared on a type it is visible within that type, declared on a method only visible within that method
Detour into companion objects
apply
method
Literal syntax
Type syntax
Functions as objects
- methods are not objects
- you can pass a function to a method or function, but you cannot pass a method to a method or function
- you can return a function from a method or function, but you cannot return a method from a method or function
- you can give a function a name with
val
- calling a function is calling the
apply
method on it - functions have other methods for composition
- convert a method to a function by using
methodName _
ormethodName(_)
- traits
Function0
etc that functions extend. Very rare to use these directly. Usually use the type syntax above.
Collection classes live in scala.collection.immutable
or scala.collection.mutable
. Some are already imported (part of Predef).
- Sequences
- Maps
- Constructing
- Querying
- Transforming
Retain order of elements (usually order that are inserted) List
, ArrayBuffer
, Vector
empty
: method on the companion object to construct an empty container
Vararg constructor: List()
List(1,2,3)
, etc.
Preprend and append elements: +:
, :+
If a method works on both mutable and immutable collections it is immutable.
Aside: operator notation vs method call notation
a.b(c)
is equivalent to a b c
Method names that end with a colon (:) are right associative when used as operators
a.b:(c)
is equivalent to c b: a
size
gets size / length
isEmpty
, nonEmpty
, etc.
find
: finds the first element that matches a predicate (a function from element to boolean). Returns an Option (because there may be no match).
filter
: finds all the elements that match a predicate. Returns a collection of the same type.
map
: transforms every element in the collection with the provided function. Number of elements and order does not change.
F[A] map (A => B) = F[B]
type equation. F
is the container type and does not change. The element type can change.
flatMap
: we can think of it as a map
followed by a flatten
.
F[A] flatMap (A => F[B]) = F[B]
type equation. F
is the container type and does not change. The element type can change.
getCustomersForLastMonth: List[Customer]
getOrdersForCustomer(customer: Customer): List[Order]
To get all the orders for the last month
getCustomersForLastMonth.flatMap(c => getOrdersForCustomer(c))
groupBy
: Groups data using a key returned by the function parameter.
foldLeft
and foldRight
: these are structural recursion
Maps associate keys with values Two type parameters: Key and Value
Map("a" -> 1, "b" -> 2, "c" -> 3): Map[String, Int]
with String
keys and Int
values
Note: a -> b
is a tuple, equivalent to (a, b)
Maps work a lot like sequences (that have a map
method, for example)
mapValues
to map just the values
get
to get a value by key (returning an Option
)
+
to add key-value pairs to the Map
val mm1: HashMap[String, Int] = HashMap(“a” -> 1, “b” -> 2) val mm2: HashMap[String, Any] = mm1
mm2 += (“c” -> “surprise!”)
mm1.get(“c”)
Use structural sharing for efficiency. Sometimes called persistent data structures.