Transcript Scala API
The Scala API
The Scala API
Scala has a reputation of being a difficult language
Some people feel that Scala is beyond what the average
programmer can master
Others say it’s no more difficult than Java
Everyone agrees that the Scala API is harder to
understand than the Java API
Some things have been added, but a lot has been simplified
This certainly makes Scala seem more difficult than it is
You don’t have to understand everything about a method in
order to use it effectively
This lecture is an attempt to de-mystify the Scala API
2
Searching
The API is search oriented; every frame has a search field
You can click on a letter and search for methods
3
Icons
C indicates a class
T indicates a trait
O indicates an object
O and C together indicate a
class with a companion object
4
Objects
A class is a template for creating objects
But if you need only one object of a given type, you can just
create it directly
object Instructor { val name = "David Matuszek" }
Scala provides a number of predefined objects, such as Console
object TryIt {
def main(args: Array[String]): Unit = {
val c = Console
c.print("Hello, console!")
}
}
Hello, console!
5
Classes
A class is a template for creating objects; it may take
parameters
Classes may be marked as case, abstract, or final
case classes have special features
To create an object, you may omit the word new
Case classes can be used in pattern matching
Case classes have automatically generated toString,
hashCode, and equals methods (which use the constructor
parameters)
abstract classes cannot be instantiated
final classes cannot be subclassed
6
Creating new objects from classes
When you define a class, you usually use the word new to create objects of that class
If you make the class a case class, you don’t need the word new
object TryIt {
def main(args: Array[String]): Unit = {
val t = new Thing(1)
}
}
class Thing(number: Int) {
println(s"I'm Thing $number!")
}
object TryIt {
def main(args: Array[String]): Unit = {
val t = Thing(1)
}
}
case class Thing(number: Int) {
println(s"I'm Thing $number!")
}
Both objects produce this output: I'm Thing 1!
7
Companion objects and classes
If a class and an object have the same name and are defined on the same source
file, they are companions
Each has access to all the features of the other
object Thing {
private var count = 0
def main(args: Array[String]): Unit = {
new Thing(1, 2, 3)
println(s"That's $count Things!")
}
}
class Thing(val numbers: Int*) {
for (n <- numbers) {
println(s"I'm Thing $n!")
Thing.count += 1
}
}
I'm Thing 1!
I'm Thing 2!
I'm Thing 3!
That's 3 Things!
8
Companion objects and static
Values and methods in a companion object are similar to
static values and methods in Java
The companion object is often a good place to put factory
methods
object Thing {
def main(args: Array[String]): Unit = {
val t = thingMaker(5)
println(t)
}
def thingMaker(n: Int) = new Thing(n)
}
case class Thing(val number: Int)
Thing(5)
9
The apply method
The apply method of a companion object typically creates an object of the
companion class
The apply method of an object typically indexes into that object
scala> val nums = List.apply(1, 2, 3, 4, 5)
nums: List[Int] = List(1, 2, 3, 4, 5)
scala> val m = Map.apply(1 -> 10, 2 -> 20)
m: scala.collection.immutable.Map[Int,Int] = Map(1 -> 10, 2
-> 20)
scala> m.apply(2)
res10: Int = 20
scala> nums.apply(2)
res9: Int = 3
thing(arg) gets translated into thing.apply(arg)
A case class has an apply method; this is why you don’t need new
10
Traits
A trait is like a Java interface, except that it can have concrete as
well as abstract methods
Traits can be “mixed in” to classes and objects
trait Stretchable {
def stretch(s: String) = s.toList.mkString(" ")
}
object Thing extends Stretchable {
def main(args: Array[String]) {
val hi = "Hello, World!"
println(stretch(hi))
}
}
H e l l o ,
W o r l d !
11
Int is in the scala package
It is abstract, so you can’t directly create an Int
It is final, so you can’t create a subclass of Int
It extends AnyVal, which is the superclass of values that
in Java are “primitives”
AnyVal extends Any, which is the “topmost” class
AnyRef also extends Any, and is the class that corresponds to
Java’s Object class
12
Operators are methods
Unlike Java, operators are methods
This means that they are all in the Scala API
In Scala, a name can be:
Composed of letters, digits, and underscores
Composed of “punctuation marks,” excluding brackets and periods
Composed of letters, digits, and underscores, then an underscore, then
punctuation marks
Composed of just about anything (even whitespace) enclosed in backquotes
For example, in the description of Any:
final def ==(arg0: Any): Boolean
Test two objects for equality. The expression x == that is equivalent to if
(x eq null) that eq null else x.equals(that).
returns true if the receiver object is equivalent to the argument; false
otherwise.
13
Parameters and return values
scala> class Animal
defined class Animal
scala> class Mammal extends Animal
defined class Mammal
scala> class Dog extends Mammal
defined class Dog
scala> object Tester {
| def identity(m: Mammal): Mammal = m
| def test = {
|
val something: Animal = identity(new Dog)
|
something
| } }
defined module Tester
scala> Tester.test
res2: Animal = Dog@563625d0
14
Function1
Function1 is declared in the Scala API as
trait Function1[-T1, +R] extends AnyRef
The –T1 indicates that the parameter may be a supertype (this is contravariance)
The +R indicates that the return value may be a subtype (this is covariance)
Functions are contravariant in their argument types and co-variant in their return types
class Animal
class Dog extends Animal
class Person
class Student extends Person
def owner(f: Dog => Person) { println("ok") }
def dogOwner(d: Dog) = new Person
def petOwner(d: Dog) = new Student
def animalOwner(a: Animal) = new Person
// exactly as expected
// return is subtype
// arg is supertype
The following calls are all legal, and all print ok:
owner(dogOwner)
owner(petOwner)
owner(animalOwner)
15
Contravariance in parameters
On the previous slide,
Dog extends Animal and Student extends Person
We also had the definitions:
def owner(f: Dog => Person) { println("ok") }
def dogOwner(d: Dog) = new Person
This is a function of exactly the type required, so it works
def petOwner(d: Dog) = new Student
The argument to this function has type Dog => Person
This is a function Dog => Student, but Student is a subclass of Person,
so it works (covariance)
def animalOwner(a: Animal) = new Person
This is a function Animal => Person, but Animal is a superclass of Dog
Since this function will take any Animal as a parameter, it will work for a Dog
(this is contravariance), and we can use it as a parameter to owner
16
Java isn’t type safe
class Animal {}
class Mammal extends Animal {}
public class Animals {
public static void main(String[] args) {
Mammal[] mammals = new Mammal[1];
Animal[] animals = mammals;
animals[0] = new Animal();
System.out.println(animals[0]);
}
}
This compiles without any problem
Exception in thread "main" java.lang.ArrayStoreException:
Animal
at Animals.main(Animals.java:9)
17
Covariance, contravariance, invariance
Covariance and contravariance are properties of collections
A collection of values of type A is covariant if it may be treated
as a collection of values of some supertype of A
A collection of values of type A is contravariant if it may be
treated as a collection of values of some subtype of A
That is, you can use a subtype of the expected type
Lists are covariant because a List[Dog] may be treated as if it were a
List[Animal]
class List [+A] extends LinearSeq[A]
That is, you can use a supertype of the expected type
trait Function1 [-T1, +R] extends AnyRef
This trait defines a function that is contravariant in its argument type, and
covariant in its return type
A collection is invariant if it is neither covariant or contravariant
18
Again, with pictures
Suppose you have a function of type B → C
And suppose you would like to give the function a value
of type A, and get back a value of type D
D
A →
B → C
→ D
A
You can do this if A is a subtype of B, and C is a
subtype of D
19
Again, with pictures
Suppose you have a function of type B → C
And suppose you would like to give the function a value
of type A, and get back a value of type D
A →
B → C
→ D
You can do this if A is a subtype of B, and C is a
subtype of D
20
Variance for mutable structures
For type safety
Read-only (source) data structures can be covariant
Write-only data types (sinks) can be contravariant
If a data structure is both readable and writeable (such as arrays in Java), it
should be invariant
Java arrays are covariant
Did Java get it wrong?
If arrays were invariant, generic operations that do not depend on the type of
object (shuffling an array, comparing two arrays for equality) would not be
possible
Generics (parameterized types) make a solution possible, but not without
breaking legacy programs
<T> boolean equalArrays (T[] a1, T[] a2);
Unlike Java, Scala has had parameterized types from the beginning
Scala does not infer variance types; that’s up to the programmer
21
Multiple parameter lists
def foldLeft [B] (z: B)(op: (B, A) ⇒ B): B
A and B are type parameters
z is of type B
op is a function that takes a B and an A and returns a B
scala> List(1, 2, 3).foldLeft(10.0)(_ + _)
res15: Double = 16.0
The parameter lists allow partial application of the function to create another
function
scala> val fold10 = (_: List[Int]).foldLeft(10.0)(_ + _)
fold10: List[Int] => Double = <function1>
Here we are supplying an indefinite receiver and an indefinite final argument
(underscores)
scala> fold10(List(1, 2, 3))
res26: Double = 16.0
22
Least Upper Bound
The notation B >: A states that B is a superclass of A
All the elements of a List must be of the “same type.”
The “cons” operator, ::, is defined in List as
def :: (x: A): List[A]
Consing a value of type A to a List[A] returns a List[A]
But there is an additional definition,
def :: [B >: A] (x: B): List[B]
If B is a superclass of A, B >: A, then adding a B to a
List[A] will result in a List[B]
23
Sir With-A-Lot
Scala objects are usually built from small pieces
Lots of inheritance, lots of “withing”
The above are buttons, allowing you to control how much is displayed
Anything, such as List above, that inherits from Seq (sequence)
or TraversableLike, will supply the important functions map,
flatMap, filter, and probably some folds
24
Use the source, Luke
Although the documentation is often difficult to understand, the
source code often is not
Scala methods tend to be short
The high-level constructs often make code easier to read
Just as Java has javadoc for producing professional-looking
documentation, Scala has scaladoc
As your code does not have the same constraints as library code, your
scaladoc files will probably not be this complicated
25
The End
26