Lecture 21: The Many Hats of Scala: OOP 10:00 AM, Mar 14, 2018

Similar documents
Overview. Elements of Programming Languages. Objects. Self-Reference

Lecture 23: Priority Queues 10:00 AM, Mar 19, 2018

Lab 8: Introduction to Scala 12:00 PM, Mar 14, 2018

Lecture 23: Priority Queues, Part 2 10:00 AM, Mar 19, 2018

COMP 250 Fall inheritance Nov. 17, 2017

CS-202 Introduction to Object Oriented Programming

Overview. Elements of Programming Languages. Objects. Self-Reference

Good Coding Practices Spring 2018

Lab 9: More Sorting Algorithms 12:00 PM, Mar 21, 2018

ITI Introduction to Computing II

Overview of OOP. Dr. Zhang COSC 1436 Summer, /18/2017

Lecture 4: Inheritence and Abstract Classes

ITI Introduction to Computing II

Inheritance and Polymorphism in Java

Data Structures (list, dictionary, tuples, sets, strings)

Lab 2: Object-Oriented Design 12:00 PM, Jan 31, 2018

Rules and syntax for inheritance. The boring stuff

Day 4. COMP1006/1406 Summer M. Jason Hinek Carleton University

COMP 110/L Lecture 19. Kyle Dewey

Lecture 16: HashTables 10:00 AM, Mar 2, 2018

Java Object Oriented Design. CSC207 Fall 2014

Lecture 24: Implementing Heaps 10:00 AM, Mar 21, 2018

Lecture 12: Dynamic Programming Part 1 10:00 AM, Feb 21, 2018

Lecture 5: Implementing Lists, Version 1

Lecture 8: Iterators and More Mutation

Unit3: Java in the large. Prepared by: Dr. Abdallah Mohamed, AOU-KW

Software Development (cs2500)

CSE 130: Spring 2012

Inheritance and Polymorphism

1B1b Inheritance. Inheritance. Agenda. Subclass and Superclass. Superclass. Generalisation & Specialisation. Shapes and Squares. 1B1b Lecture Slides

09/02/2013 TYPE CHECKING AND CASTING. Lecture 5 CS2110 Spring 2013

Lab 10: Sockets 12:00 PM, Apr 4, 2018

CS2102: Lecture on Abstract Classes and Inheritance. Kathi Fisler

Inheritance. Transitivity

15 Multiple Inheritance and Traits

Everything is an object. Almost, but all objects are of type Object!

Lesson 10A OOP Fundamentals. By John B. Owen All rights reserved 2011, revised 2014

More About Objects. Zheng-Liang Lu Java Programming 255 / 282

CS125 : Introduction to Computer Science. Lecture Notes #11 Procedural Composition and Abstraction. c 2005, 2004 Jason Zych

CS152: Programming Languages. Lecture 24 Bounded Polymorphism; Classless OOP. Dan Grossman Spring 2011

PROGRAMMING LANGUAGE 2

Software Design and Analysis for Engineers

Programming in Scala Mixin inheritance Summer 2008

Relationships Between Real Things. CSE 143 Java. Common Relationship Patterns. Composition: "has a" CSE143 Sp Student.

Object-Oriented Design Lecture 16 CS 3500 Fall 2010 (Pucella) Friday, Nov 12, 2010

17 Multiple Inheritance and ADT Extensions

Inheritance. Inheritance Reserved word protected Reserved word super Overriding methods Class Hierarchies Reading for this lecture: L&L

Lecture 22: Garbage Collection 10:00 AM, Mar 16, 2018

What is Inheritance?

CSE 341, Autumn 2015, Ruby Introduction Summary

Relationships Between Real Things CSE 143. Common Relationship Patterns. Employee. Supervisor

17 From Delegation to Inheritance

Inheritance and Polymorphism

CS 251 Intermediate Programming Methods and Classes

CS 251 Intermediate Programming Methods and More

Software Engineering /48

CS558 Programming Languages

CMPSCI 187: Programming With Data Structures. Lecture 6: The StringLog ADT David Mix Barrington 17 September 2012

Object-oriented programming. and data-structures CS/ENGRD 2110 SUMMER 2018

The name of our class will be Yo. Type that in where it says Class Name. Don t hit the OK button yet.

Working with Objects. Overview. This chapter covers. ! Overview! Properties and Fields! Initialization! Constructors! Assignment

Programming Exercise 14: Inheritance and Polymorphism

Lab 4 Due April 18 th

Relationships Between Real Things CSC 143. Common Relationship Patterns. Composition: "has a" CSC Employee. Supervisor

ECE 122. Engineering Problem Solving with Java

The 10 Minute Guide to Object Oriented Programming

Scala Style Guide Spring 2018

Day 5. COMP1006/1406 Summer M. Jason Hinek Carleton University

Objects, Subclassing, Subtyping, and Inheritance

Object-Oriented Programming More Inheritance

Object Oriented Programming Part II of II. Steve Ryder Session 8352 JSR Systems (JSR)

CS 251 Intermediate Programming Inheritance

First IS-A Relationship: Inheritance

Lecture 14: Exceptions 10:00 AM, Feb 26, 2018

Why use inheritance? The most important slide of the lecture. Programming in C++ Reasons for Inheritance (revision) Inheritance in C++

Software Paradigms (Lesson 3) Object-Oriented Paradigm (2)

More Relationships Between Classes

CSE341, Fall 2011, Lecture 26 Summary

Object Oriented Features. Inheritance. Inheritance. CS257 Computer Science I Kevin Sahr, PhD. Lecture 10: Inheritance

Lecture 7: Implementing Lists, Version 2

Programming in the large

Introduction to Scientific Programming with C++

CPS 506 Comparative Programming Languages. Programming Language

16 Multiple Inheritance and Extending ADTs

CMSC131. Inheritance. Object. When we talked about Object, I mentioned that all Java classes are "built" on top of that.

Generalized Code. Fall 2011 (Honors) 2

Programming in C++ Prof. Partha Pratim Das Department of Computer Science and Engineering Indian Institute of Technology, Kharagpur

CS Programming Languages: Scala

Programming Languages and Techniques (CIS120)

CS1004: Intro to CS in Java, Spring 2005

Inheritance, Polymorphism, and Interfaces

After a lecture on cosmology and the structure of the solar system, William James was accosted by a little old lady.

Carleton University COMP1406/1006, Summer 2013 Tutorial 2

Inheritance and Interfaces

Inheritance (Part 5) Odds and ends

D Programming Language

COMP200 INTERFACES. OOP using Java, from slides by Shayan Javed

Need to store a list of shapes, each of which could be a circle, rectangle, or triangle

CMSC201 Computer Science I for Majors

TOPIC 2 INTRODUCTION TO JAVA AND DR JAVA

Transcription:

CS18 Integrated Introduction to Computer Science Fisler, Nelson Lecture 21: The Many Hats of Scala: OOP 10:00 AM, Mar 14, 2018 Contents 1 Mutation in the Doghouse 1 1.1 Aside: Access Modifiers.................................. 2 2 Multiple Inheritance? 3 3 Traits as Interfaces 4 4 Traits as Mixins: Overcoming the Diamond Problem 4 Objectives By the end of these notes, you will know: ˆ Scala OOP features such as traits and how they work By the end of these notes, you will be able to: ˆ Write and mix in traits in your Scala programs The last lecture gave us a taste of imperative programming in Java. Now let s have a go at object-oriented programming. First, we ll revisit the issue of var vs. val in an OOP context. 1 Mutation in the Doghouse Let s write a class representing a very basic Dog. Dogs can each speak; by default they say woof. They also each have a height, and when they feed, they grow. Since we want a dog s height to be mutable, we declare the height field as var: class Dog(var height: Int) { def speak: String = "woof" def feed() { height += 1 Now let s create two dogs (either in the REPL or in our App object). We ll make one val and one var:

val dog1 = new Dog(100) var dog2 = new Dog(200) Let s look closely at what we ve actually done. We ve created two distinct Dog objects. Each of these resides at its own memory location. The identifiers dog1 and dog2 reference those addresses 1. In this example, we ll call the two addresses 0x1000 and 0x1010. 0x1000 0x1010 var height = 100 var height = 200 val dog1 0x1000 var dog2 0x1010 Now let s feed the dogs and print out their height: scala> dog1.feed scala> dog2.feed scala> println(dog1.height) 101 scala> println(dog2.height) 102 Notice that although dog1 is declared as a val, that doesn t keep us from calling that dog s feed method. Since all Dogs have a mutable height field, both dogs grow when fed! So what good is val in this case? It s making something immutable, but that something is clearly not a Dog. To answer this question, we ll create a third dog. val dog3 = new Dog(300) Because dog2 is declared as a var, we can overwrite the address it currently holds: dog2 = dog3 The name dog2 now references the latest, third dog, and the reference to the second dog at 0x1010 is now lost. However, if we try to overwrite dog1 with the third dog s location, Scala will complain dog1 is a val and can t be mutated! Whether a data structure (like a dog or a database) is mutable depends on how its fields and methods are defined. Similarly, whether a reference is mutable depends on how that reference is declared. An immutable reference could point to a mutable object, and mutable references can point to immutable objects! Just make sure that you know what is and isn t mutable add mutability only where it makes sense! 1 Tim likes to think of these as doghouses. 2

1.1 Aside: Access Modifiers In Scala, there is no need to write getters and setters because all attributes of a class can be accessed via the dot operation, assuming those attributes are not explicitly declared to be private. Anything not explicitly declared to be private is public by default in Scala. Of course, if a field is declared as a val, it cannot be set! If a field is declared without either val or var, Scala will make it immutable (i.e., a val) but not allow it to be read, either. (To prevent field reads on a field declared with val or var, use the private keyword.) One exception to this policy occurs in case classes, where all fields are vals and thus immutable and readable (which echoes the way we use case classes!) 2 Multiple Inheritance? In Java, classes can only extend one other class, although they can implement any number of interfaces. Let s take a look at why Java does this 2. In object-oriented design, multiple inheritance raises a dangerous trap called the diamond problem. This problem arises in how a subtype perceives a supertype which is common to two supertypes of the subtype. To make this inheritance pattern more clear, look at the figure below, which, sure enough, is shaped like a diamond: To restate the diamond problem: How does Type D perceive what has been inherited from TypeA if both of Type B and Type C inherit from Type A? In particular, If Type B and Type C both provide implementations of a method defined in Type A, to which implementation are we referring when a Type D object invokes the method? Java has a straightforward solution to the diamond problem. It does not support multiple inheritance among classes. On the contrary, every Java class has one and only one base class (i.e., immediate superclass). 3 Consequently, there is never a question of which implementation of an inherited method to invoke. 4 2 When a subtype inherits from multiple supertypes, this is called multiple inheritance 3 Except Object, which sits at the root of the Java hierarchy and has no base class. 4 A Java class can implement multiple interfaces (as they provide only empty promises!). This is Java s answer to multiple inheritance. 3

Scala also does not support multiple inheritance. As in Java, every Scala class has one and only one base class. On the other hand, Scala supports something that is even more powerful than multiple inheritance, called traits. Traits can be used to do what Java interfaces do: provide a specification of methods that must be provided. But they can also be used for much more. In particular, something called stackable modifications. We ll address both of these via the above whimsical canine example. 3 Traits as Interfaces Look at the Dog class again. Is there any particular reason that the speak method should be unique to Dogs? There are many kinds of animals that might speak, although perhaps none so eager to do so. In Java, we could create an interface for the speak method, which Dog (and all other noisy animals) implement. In Scala, we will do the same thing it s just called a trait instead! trait Speaker { def speak: String Now all we have to do is modify the first line of Dog: class Dog(var height: Int) extends Speaker {... Note that Dog didn t implement the trait, but extended it. So far, we have talked only of pure interfaces interfaces in which no methods are implemented. In Java, interfaces must be pure. But in Scala, traits are not pure: they can include implementation details more on this soon! Because Scala blurs the distinction between interfaces and abstract classes, it has also dropped the distinction between the keywords implements and extends. In Scala, only one of these keywords is used, namely extends. 4 Traits as Mixins: Overcoming the Diamond Problem Let s go a bit further and define two subclasses of Dog: Labrador and Mastiff. These each have a custom speak method. Labradors are so energetic that their speech comes out surrounded by stars, and Mastiffs are so loud that their speech is in uppercase. Furthermore, Labradors are always friendly they can wag their tail at people. class Labrador(height: Int) extends Dog(height) { override def speak: String = Console.RED + "woof" + Console.RESET def wag(name: String): String = "The dog wags its tail at "+name class Mastiff(height: Int) extends Dog(height) { override def speak: String = "WOOF" 4

This doesn t seem fair! Perhaps not all Mastiffs are friendly, but surely some of them are. Moreover, what if we decide to add more types of dog, some of which are as friendly as Labradors? This won t do at all. To solve our dilemma, we pull the notion of friendliness out of the Labrador class, making it a trait. Every Labrador ever created will mix in that trait: trait Friendly extends Dog { def wag(name: String): String = "The dog wags its tail at "+name class Labrador(height: Int) extends Dog(height) with Friendly { override def speak: String = "*woof*" Notice how we made Friendly extend Dog. If traits were really just interfaces, this would be backwards! But traits can contain behavior, too. Traits like this are said to mix in behavior (and hence we call them mixins). (To avoid potential confusion, we enforce that, for now, only Dogs can exhibit friendliness by having the trait extend Dog.) Now if we create a lab and a mastiff, both can speak, but only the lab can wag: val lab = new Labrador(100) val m = new Mastiff(200) scala> println(lab.speak) *woof* scala> println(m.speak) WOOF scala> println(lab.wag("tim")) The dog wags its tail at Tim This still isn t fair! What about all the friendly mastiffs out there? Let s pick one at random: Bob. Bob the friendly mastiff. When he was born, someone mixed in the Friendly trait. Yes, we don t have to apply traits only at class definition we can apply them at object instantiation, like this: @scala val bobthefriendlymastiff = new Mastiff(200) with Friendly scala> println(bobthefriendlymastiff.wag("tim")) The dog wags its tail at Tim Great! But now that we re adding traits, we should take another look at speak. After all, colorful or loud speech shouldn t have to be reimplemented every time a type of dog uses them. We ll make these traits, too. There s only one problem: adding asterisks and capitalization are add-ons that modify an existing speech pattern. We don t want to just say: trait EmphaticSpeaker extends Dog { override def speak: String = "*woof*" because we might be mixing in EmphaticSpeaker with a kind of dog that doesn t say woof, but something else entirely 5. If we hard-code woof in the trait, it will override the speech of whatever 5 Meow? 5

we mix it into. Fortunately, Scala gives us a convenient way to address this problem: super. We can use super. speak to invoke the original, pre-mixin class s speak method: trait EmphaticSpeaker extends Dog { override def speak: String = "*" + super.speak + "*" trait LoudSpeaker extends Dog { override def speak: String = super.speak.touppercase We can even have some kinds of dog elongate their lowercase vowels: trait LongLowercaseVowelSpeaker extends Dog { override def speak: String = super.speak.tolist.flatmap( // Note: not tostring c => if("aeiou".indexof(c) < 0) List(c) else List(c,c,c)).mkString Now we can create dogs with whatever traits we might want, even a hypothetical dog that says meow emphatically. class ConfusedDog(height: Int) extends Dog(height) with EmphaticSpeaker { override def speak: String = "meow" It s easy to mix in multiple traits. For instance: val dog3 = new Dog(100) with LoudSpeaker with LongLowercaseVowelSpeaker val dog4 = new Dog(100) with LongLowercaseVowelSpeaker with LoudSpeaker val dog5 = new Dog(100) with LongLowercaseVowelSpeaker with LoudSpeaker with EmphaticSpeaker scala> println(dog3.speak) WOOF scala> println(dog4.speak) WOOOOOOF scala> println(dog5.speak) *WOOOOOOF* Notice, however, that mixins can interfere with one another. If LoudSpeaker gets applied to the original string "woof" first, there will be no lower case vowels for LongLowercaseVowelSpeaker to match and elongate. Scala resolves this issue via linearization: the rightmost mixin receives the initial method call. Its parent is the class with the next innermost mixin, and so on. This is all done via the class hierarchy: the rightmost mixin is just the last class-modification in the chain. This figure shows the inheritance for the mixins of dog5: 6

Dog Dog with LongLowercaseVowelSpeaker Dog with LongLowercaseVowelSpeaker with LoudSpeaker Dog with LongLowercaseVowelSpeaker with LoudSpeaker with RedSpeaker When we call dog5.speak, we are really calling the speak method in EmphaticSpeaker, in a situation where super is a Dog with LongLowercaseVowelSpeaker with Loudspeaker. So when that EmphaticSpeaker s method calls super.speak, LoudSpeaker gets its turn, and so on. Hence the term stackable modifications : mixing in multiple traits implicitly builds a stack of superclasses. Dog3 doesn t elongate its vowels since those vowels are converted to uppercase before LongLowercaseVowelSpeaker s flatmap operation applies. Dog4 and Dog5, however, are alright. Finally, a word of warning. Suppose we create another breed of dog: class Terrier(height: Int) extends Dog(height) with EmphaticSpeaker { override def speak: String = "grrrrr" The first line here should not be read as Terrier extends Dog, and then mixes in EmphaticSpeaker. The class that Terrier extends is Dog with EmphaticSpeaker already mixed in. Why does this matter? Just ask a Terrier to speak: scala> val thomas = new Terrier(50) scala> println(thomas.speak) grrrrr Not very emphatic at all. This is because we ve overridden the entire chain of mixins. The speak method defined in EmphaticSpeaker simply never gets called: the Terrier s redefinition overrides it. Final Observation Whereas a class can only inherit from one superclass, a class can mix in any number of traits. This allows Scala to work around the diamond problem. The flexible behaviors seen here, which were achieved by mixing in traits in various orders, could not be easily achieved via multiple inheritance. 7

Please let us know if you find any mistakes, inconsistencies, or confusing language in this or any other CS18 document by filling out the anonymous feedback form: http://cs.brown.edu/ courses/cs018/feedback. 8