Classes and Objects and Traits
Download
Report
Transcript Classes and Objects and Traits
Classes and Objects and Traits
And Other Miscellany
29-Mar-16
Classes, objects, case classes, traits
A class is a template for objects
An object is an object created directly, not from a class
All constructor parameters are implicitly val (fields)
Free implementations of equals, hashCode, toString, copy, and a factory
method
Case classes support pattern matching
A trait is something that can be “mixed in” to a class
You can “talk to the object”
Notice that, in this case, the name of the object begins with a capital letter
A case class is just a class with extra goodies
Use the class to create “objects” or “instances” of the class
You don’t “talk to the class,” you talk to objects created from the class
Traits may include both abstract and concrete methods
In Scala you can nest almost anything in anything else
This is actually a problem, until you learn when to do and when not to do this
Classes
Syntax: class ClassName(parameters) { body }
or class ClassName(parameters) extends ClassName { body }
The class definition is the (primary) constructor
Parameters and body are optional
Parameters, if any, are marked with:
var
val
A val parameter will create a field and a getter, but no setter
Neither val nor var
A var parameter will cause a field, getter, and setter to be included:
var p: Int gives the methods p: () => Int and p_=: Int => ()
Can be used within the body of the class, but not create a field or any methods
Inherited methods may be overridden in the class
Constructors
The class definition is the primary constructor
When creating a new object, the code within the class is executed
vars and vals are created for the new object
Function definitions are made available to the new object
“Loose” code, not contained within a function, is evaluated
Auxiliary (additional) constructors have the syntax
This may include any Scala: Loops, I/O, etc.
def this(parameters) {
call to a constructor declared earlier (this is required, and must be first)
rest of code
}
Use the keyword new to call a constructor
new Person("Dave")
Examples I
scala> class Person(val firstName:String, var lastName: String,
age: Int)
defined class Person
scala> val mary = new Person("Mary", "Smith", 23)
mary: Person = Person@d73c3c
scala> mary.firstName
res22: String = Mary
scala> mary.lastName
res23: String = Smith
scala> mary.firstName = "Sally"
<console>:7: error: reassignment to val
scala> mary.lastName = "Jones"
res24: String = Jones
scala> mary.age
<console>:8: error: value age is not a member of Person
scala> mary.lastName
res25: String = Jones
5
Examples II
Again, but this time with a method:
scala> class Person(val firstName:String,
|
var lastName: String, age: Int) {
| override def toString = firstName +
|
" " + lastName + ", age " + age
| }
defined class Person
scala> val mary = new Person("Mary", "Smith", 23)
mary: Person = Mary Smith, age 23
scala> println(mary)
Mary Smith, age 23
6
Object
An object is defined similar to the way that a class is
defined, but it cannot take parameters
Syntax: object ObjectName { body }
A program’s main method is defined in an object:
def main(args: Array[String]) { body }
Thus, a “complete” program requires at least one object
definition
Companion objects
The companion object of a class
The purpose of a companion object is
has the same name as the class
is defined in the same file as the class
To hold information that is the same for all objects of the class (so that the
exact same data is not duplicated many times)
To hold methods useful to the class that do not depend on any specific
object of the class
The class and its companion object can access each other’s
private fields and methods
In the class, access to the fields and methods in the companion
object must be qualified with the name of the object
Abstract classes
To define a method as abstract, simply omit its body
To define a field as abstract, omit its initial value
A class containing abstract methods or fields must be
declared abstract
def someMethod(n: Int)
abstract class LivingThing { … }
An abstract class is one that cannot be instantiated
In a concrete subclass, you do not need the override
keyword
Case classes
Syntax: case class ClassName(parameters) { body }
All the parameters are implicitly val
A parameter can be explicitly declared as var (not recommended)
toString, equals, hashCode, and copy are generated
(unless you supply them)
apply and unapply are also generated
apply lets you omit the word new when you create objects
unapply lets you use the objects in pattern matching
Case classes can be pattern matched
scala> case class Person(age: Int, name: String)
defined class Person
scala> val dave = Person(40, "Dave")
dave: Person = Person(40,Dave)
scala> dave match {
| case Person(a, n) if a > 30 => println(n + " is old!")
| case _ => println("Whatever")
| }
Dave is old!
scala> val quinn = Person(25, "Quinn")
quinn: Person = Person(25,Quinn)
scala> quinn match {
| case Person(a, n) if a > 30 => println(n + " is old!")
| case _ => println("Whatever")
| }
Whatever
Traits
Traits are like classes that can be “mixed in” to other classes
Syntax: trait TraitName { body }
Traits may have concrete (defined) methods
A class extends exactly one other class, but may with any
number of traits
Syntax:
class ClassName(parameters) extends OtherClass with Trait1,
…, TraitN { body of class }
class ClassName(parameters) extends Trait1 with Trait2, …,
TraitN { body of class }
I consider this use of extends to be confusing
Pattern matching with match
You have seen pattern matching with match and literals
today match {
case "Saturday" => println("Party! Party! Party!")
case "Sunday" => println("Pray....")
case day => println(day + " is a workday. :( ")
}
You can match with types
something match {
case x: Int => println("I'm the integer " + x)
case x: String =>
println("I'm the String \"" + x + "\"")
println("My length is " + x.length)
case _ => println("I don't know what I am! :( ")
}
Pattern matching in assignments
You can pattern match on tuples:
val (a, b, c) = (3, 5, 7)
= 3
= 5
= 7
But…
scala>
a: Int
b: Int
c: Int
scala> val a,
a: (Int, Int,
b: (Int, Int,
c: (Int, Int,
b, c
Int)
Int)
Int)
=
=
=
=
(3, 5, 7)
(3,5,7)
(3,5,7)
(3,5,7)
You can pattern match on lists:
scala> val list = List("once", "upon", "a", "time")
list: List[java.lang.String] = List(once, upon, a, time)
scala> val first :: second :: rest = list
first: java.lang.String = once
second: java.lang.String = upon
rest: List[java.lang.String] = List(a, time)
Operations and methods
As operation
As method call
Unary prefix
scala> -5
res4: Int = -5
scala> 5 unary_res5: Int = -5
Unary
scala> " abc " trim
res6: java.lang.String =
abc
scala> " abc ".trim()
res7: java.lang.String =
abc
Binary
scala> "abc" + "xyz"
res8: java.lang.String =
abcxyz
scala> "abc".+("xyz")
res9: java.lang.String =
abcxyz
scala> "abcdef" substring
2
res10: java.lang.String =
cdef
scala>
"abcdef".substring(2)
res11: java.lang.String =
cdef
scala> "abcdef" substring
(1, 3)
res12: java.lang.String =
bc
scala>
"abcdef".substring(1, 3)
res13: java.lang.String =
bc
>2 operands
15
Parameters in braces
A block consists of any number of statements inside braces, { }
When a method takes just one parameter (in addition to the object), you can
put that parameter inside braces instead of parentheses
The last value in the block is the value of the block
Parentheses, ( ), can’t enclose multiple statements
scala> "abcdefg" substring { 2 }
res0: java.lang.String = cdefg
This example is pointless and looks silly
Sometimes, you may want to compute that parameter by a series of statements
scala> println {
| var x = 2
| while (x < 1000) x *= 2
| x
| }
1024
This isn’t a great example either, but it does make the point
16
Methods with no parameters
You can define a “parameterless” method:
You can define an “empty paren” method:
scala> def hello = println("Hello!")
hello: Unit
scala> hello
Hello!
scala> hello()
<console>:7: error: hello of type Unit does not take parameters
scala> def hi() = println("Hi!")
hi: ()Unit
scala> hi
Hi!
scala> hi()
Hi!
If you define a method without parentheses, you can’t call it with parentheses
You can replace a parameterless method with an empty paren method, without
affecting user code (but not vice versa)
17
Uniform access
In Java, the length of an array is a field, so you have to say
myArray.length; but the length of a String is a field, so you
have to say myString.length()
However, if I say foo = bar, or println(bar), I am using
bar like a variable, so I expect bar to act like a variable:
This violates the principle of uniform access: The user shouldn’t have to
know whether it’s a field or a method
bar should not do I/O
bar should not change mutable state
bar should not depend on values in mutable state
In other words, if bar is a function, it should be a pure function
Scala convention: When you call a method that does one of the
above (impure) things, use parentheses
18
Types
Types can be “aliased” (named)
type Word = String
type Sentence = List[Word]
type Paragraph = List[Sentence]
This is a simple thing that can be extremely helpful
when dealing with complex data types
The End