Classes and Objects and Traits And Other Miscellany 7-Nov-15 Classes, objects, case classes, traits  A class is a template for objects    An object is.

Download Report

Transcript Classes and Objects and Traits And Other Miscellany 7-Nov-15 Classes, objects, case classes, traits  A class is a template for objects    An object is.

Classes and Objects and Traits And Other Miscellany

26-Apr-20

Classes, objects, case classes, traits

     A class is a template for objects   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 An object is an object created directly, not from 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   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  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

The class definition

is

the (primary) constructor  Parameters and body are optional {

body

Parameters, if any, are marked with:    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 => () val  A val parameter will create a field and a getter, but no setter Neither val nor var  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 var s and val s are created for the new object Function definitions are made available to the new object “Loose” code, not contained within a function, is evaluated  This may include any Scala: Loops, I/O, etc.

 Auxiliary (additional) constructors have the syntax  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" :7: error: reassignment to val scala> mary.lastName = "Jones" res24: String = Jones scala> mary.age

: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  has the same name as the class  is defined in the same file as the class The

purpose

of a companion object is   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  def someMethod(n: Int)  To define a field as abstract, omit its initial value  A class containing abstract methods or fields must be declared abstract  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 number of traits any Syntax:    class

ClassName

(

parameters

) extends

OtherClass

…,

TraitN

{

body of class

} with

Trait1

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:  scala> val (a, b, c) = (3, 5, 7) a: Int = 3 b: Int = 5 c: Int = 7 But…  scala> val a, b, c = (3, 5, 7) a: (Int, Int, Int) = (3,5,7) b: (Int, Int, Int) = (3,5,7) c: (Int, Int, Int) = (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)

Unary prefix Unary Binary

Operations and methods

>2 operands As operation

scala> -5 res4: Int = -5 scala> " abc " trim res6: java.lang.String = abc scala> "abc" + "xyz" res8: java.lang.String = abcxyz

As method call

scala> 5 unary_ res5: Int = -5 scala> " abc ".trim() res7: java.lang.String = abc 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 15

Parameters in braces

   A block consists of any number of statements inside braces, { }   The last value in the block is the value of the block Parentheses, ( ) , can’t enclose multiple statements When a method takes

just one

parameter (in addition to the object), you can put that parameter inside braces instead of parentheses  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:    scala > def hello = println("Hello!") hello: Unit scala> hello Hello!

scala> hello() :7: error: hello of type Unit does not take parameters You can define an “empty paren” method:    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()  This violates the principle of uniform access: The user shouldn’t have to know whether it’s a field or a method 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:    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