150 likes | 353 Views
Cases and Classes and Case Classes. And Other Miscellany. Classes and constructors, I. Every class has a constructor, which you write (no “invisible” constructors, as in Java) scala> class Person(val firstName:String, var lastName: String, age: Int) defined class Person
E N D
Cases and Classes and Case Classes And Other Miscellany
Classes and constructors, I • Every class has a constructor, which you write (no “invisible” constructors, as in Java) • scala> class Person(val firstName:String, var lastName: String, age: Int)defined class Person • The above is complete; no braces are needed unless you want to add code, fields, or methods • The above defines: • firstName(immutable) with getter function firstName • lastName(mutable) with getter function lastNameand setter function lastName_= • The method name uses an underscore, but you use it as an ordinary assignment, for example, lastName = "Smith" • Nothing for age (it can be used as a val in the class)
Classes and constructors, II • Syntax: class ClassName(parameters) { body } • TheClassName should begin with a capital letter and be CamelCase • This is the constructor, and it has parameters • A var parameter will cause a field, getter, and setter to be included:var p: Int gives the methods p: () => Int and p_=: Int => () • These methods can be redefined inside the method • A val parameter will create a field and a getter, but no setter • A parameter with neither val nor var does not create a field or any methods, but it can be used within the body of the class • When a new object of this class is defined, the fields are created, the methods are defined, and any “loose” code (not within a def) is executed
Classes and constructors, III • 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.firstNameres22: String = Mary • scala> mary.lastNameres23: 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.lastNameres25: String = Jones
Classes and constructors, IV • 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
Auxiliary constructors • A class (as on the previous slide) defines its primary constructor • You can have additional, auxiliary constructors • The first statement within an auxiliary constructor must be a call to another auxiliary constructor, or to the primary constructor • Thus, every object creation eventually ends up at the primary constructor • Syntax: def this(parameters1) { this(parameters2)… more code… } • Since these are overloaded constructors, parameters1 and parameters2 must be different
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 = 3b: Int = 5c: 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 = listfirst: java.lang.String = oncesecond: java.lang.String = uponrest: List[java.lang.String] = List(a, time)
Case classes • If you declare a class as a case class, you get some extra features: • It adds a factory method with the name of the class, so you can omit the word new when you create a new object • All constructor parameters are implicitly val • You get reasonable implementations of toString, hashCode, and equals “for free” • Example: • scala> case class Person(firstName: String, lastName: String)defined class Person • scala> val jane = new Person("Jane", "Eyre")jane: Person = Person(Jane,Eyre) • scala> val Person(f, l) = janef: String = Janel: String = Eyre • scala> println(jane)Person(Jane,Eyre)
Case classes can be pattern matched • scala> case class Person(age: Int, name: String)defined class Personscala> 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
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, 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
Methods with no parameters • You can define a “parameterless” method: • scala> def hello = println("Hello!")hello: Unit • scala> helloHello! • scala> hello()<console>: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> hiHi! • 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)
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 barlike 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 baris 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