Lab 3: Interfaces and Polymorphism We've had four weeks of CS15 and by now, the buzzwords of Object-Oriented Programming (OOP) may be familiar to you. OOP is an extremely powerful and quickly growing modeling paradigm that makes programming large scale applications much easier than before. For the remainder of the course, a good understanding of OOP concepts will be integral to designing and implementing the assignments. This lab is about learning how to make your life as a programmer much easier by writing flexible and reusable code through interfaces and polymorphism. Goal: Learn how to implement polymorphism using interfaces. Life before Polymorphism Let's say you are running a little low on funds, and decided to earn some money and exercise your creative muscles at the same time by opening a car painting business! Since you are a very savvy programmer, you're going to write a program that will do the painting for you. You'll begin by finding cars that need to be painted. Because of the support code for this lab, it is very important to follow the directions in the stencil and in the checkpoints! Name methods exactly as asked and instantiate objects in the given order. Getting Started ● Run cs015_install lab3 to get the stencil for this lab. ● Open up the lab3 directory in Atom. You should see five stencil files: App.java , Car.java , Fish.java , Incredibles.java , and PaintShop.java . ● We'll concentrate on the Car.java file for now. The Car has the following components: roof , body , door , and hood ● Add a paint(...) method to the Car class. ○ It should take in a javafx.scene.paint.Col or as a parameter. ○ It should not return anything. ○ Inside this method you will want to set the color of the Car 's components listed above. Each component has a setColor(...) method that takes a javafx.scene.paint.Color as a parameter. ● Save the code you added to the Car class. Double-check that there are no errors. Now that we have a car, let's figure out how to paint it! Painting Cars ● Add an instance variable to the PaintShop.java class to store the current color. ● Create an accessor method and mutator method for the current color instance variable. This should be very similar to what you did in LiteBrite. ○ Recall that, by convention, accessors are named “getValue() ” (in this case, getColor() ) and mutators are named “setValue(...) ” (in this case, setColor(...) ) ● Add a paintCar(...) method to the PaintShop ○ It should take in a Car as a parameter. ○ It should not return anything. ○ It should paint the Car based on the "current color". ■ HINT: use the Car 's paint(...) method ○ Remember to save! ● To test what you've done by, begin by instantiating a Car object in the managePaintShop method of App.java . ● In order for the the instance of PaintShop (paintShop ) in the managePaintShop method’s parameters to "know about" the Car (i.e. to set up an association with the Car ), use the PaintShop 's add(...) method and pass it the Car you've just created. ○ Question: where is this add(...) method defined? (Think inheritance). ● Save, compile and run the application. You can paint the Car different colors by selecting a color from the ColorPicker and then clicking on it. Your painting business became very successful and you want to expand your business to paint other things like: walls, roads, tables, chairs, beds, ceilings, squares, circles, and everything else in the world that can be painted. Yes, it seems ambitious, but with Java at your side anything is possible! Your new customers request that you paint some superheroes. Painting Incredibles Hint: This is very, very similar to what you did above. ● Write a paint(...) method in your Incredibles class (Incredibles.java ) to color in its components. Save! ○ Like the Car components, each of the Incredibles has a setColor(...) method. ● Write a paintIncredibles(...) method in the PaintShop class (PaintShop.java ). Save! ● Instantiate an instance of Incredibles in the managePaintShop method of App.java and add it to the paintShop (just like you did with the Car instance). Save! ● Run the application. You can control which item you are painting using the radio buttons at the bottom. This design so far has a serious problem. Your PaintShop class is very spiffy, and you want everyone to benefit from your hard work. The only problem is, every time someone wants to paint something new, you are going to have to add a new paintThing(...) method in the PaintShop class to paint the new item. If someone wanted to paint a door, you would need to add a paintDoor(...) method, if someone wanted to paint a house, you would need to add a paintHouse(...) method… and you get the idea. Wouldn't it be nice to just have one method to paint all paintable objects? The issue is that the PaintShop 's paintThing(...) method takes in a parameter: the object it is supposed to paint. For paintCar(...) it took in a Car , for paintIncredibles(...) , it took in an instance of the Incredibles . What type should it be for the generic version? Tricky, tricky, tricky... One way to do it would be to have classes inherit from a superclass PaintObject that can be painted. This way, we could write our paint method as paintThing(PaintObject paintObject) and the method could accept any subclass of PaintObject as an argument. However, there are a few problems with this design. Firstly, since Java classes can only inherit from a maximum of one class, if a class is the subclass of PaintObject , then it cannot be the subclass of anything else. This is the exact situation we are in. The Car and Incredibles classes already extend from another class (see extends in the class definition!), so they can't be the subclass of anything else. Secondly, the relationship between the different subclasses is a weak one; the only thing they have in common is that they can be painted. Using inheritance here would be limiting and unnecessary. Making a Method Generic Welcome to Interfaces Interfaces are like blank classes with only abstract methods. In essence, an interface is like a "contract" for a class, specifying the methods that a class implementing it must have. However, while a class can only subclass a single normal class, it can implement any number of interfaces. Any class that implements an interface must implement all the methods of that interface. Most of the time, Interfaces are named after the capabilities they enforce: amongst the many possibilities are Sizable , Movable , Cookable , and... Paintable . Back to our example, the PaintShop 's paintCar(...) method doesn't really care that it takes in a Car , it only cares that the Car has a paint(...) method. Likewise, the same applies for the paintIncredibles(...) method. In fact, the only thing the paintSomething(...) method cares about is that the argument has a paint(...) method. Interfaces are designed to solve this exact problem by specifying the methods a class must have. Your First Interface ● Write a Paintable interface that has a paint(...) method that takes in a javafx.scene.paint.Color as the parameter. Review the lecture slides on Interfaces to help you write your Paintable interface. Note: Interfaces only provide the method signatures. Like classes, they have to be saved as a separate java file (ex: Movable interface has to be saved as Movable.java ). ● Implement the Paintable interface in your Car and Incredibles classes. Since they both already define the paint(...) method, you don't need to write one. Checkpoint 1: Check your interface with a TA! Now that the Car and Incredibles are Paintable , let's make the appropriate changes inside the PaintShop Class. Painting Generically ● Comment out the paintCar(...) method and the paintIncredibles(...) methods from the PaintShop class. Save! ● Write a generic paintObject(...) method in the PaintShop class that takes in one parameter. Consider: What should the parameter's type be? How would you write the body of the method? ● Save and run the app. It should behave the exact same way as last time. Now that we have all this set up, let's really put it to use and see why that entire interface work paid off... Painting Nemo ● Edit the Fish.java file so that it implements the Paintable interface. Save and compile! ● Look at your error list. You should get an error that says the Fish class has not defined the paint(...) method. Right! If you implement an interface, you must define all of its methods. ● Write a paint(...) method in Fish.java . Call setColor(...) on the Fish 's components. Save! ● Create an instance of Fish in the managePaintShop method of App.java . and tell the PaintShop about the Fish . Save and compile! ● Run the application to see the results of your hard work It works! No changes had to be made to the PaintShop ; you don't need a paintFish(...) method. Summary The purpose of this lab was to teach you how to implement polymorphism using interfaces. The idea behind polymorphism is that instances of objects can be different types at the same time. Originally, the parameter passed to the PaintShop 's paintCar(...) method was a Car . By adding the interface, the object was not only of type Car , but also of type Paintable , a much more generic type that can be common for a lot of objects, such as cars, superheros, and marine animals. The Paintable interface factors out painting commonality. This is a widely used design pattern to write more generic code, allowing the programmer to write one method instead of many. Some final remarks about interfaces: Interfaces can also extend from other interfaces. For example, if you had a Singable interface that required a sing() method, you could extend this interface to create a Musical interface. It could look as follows: public interface Musical extends Singable { public void act(); public void beDramatic(); } Anything that implemented the Musical interface would have to define all the methods in Musical as well as all the methods in Singable . Don't forget, a class can implement multiple interfaces. The Car class you wrote could also implement a Transporter interface, as well as any other you deem necessary. You would separate each additional interface with a comma. The syntax to do that would be: public class Car extends cs015.labs.labInterPoly.Car implements Paintable, Transporter { /* Code here. */ } Checkpoint 2: Check your PaintShop program with a TA! The Javadocs Javadocs is the documentation of the entire Java API (Application Programming Interface), a vast collection of built-in classes for programmers to use. You’re probably familiar with a couple of these classes by now, such as String and javafx.scene.paint.Color. Javadocs tell you which classes you can use and how you should use them, as well as providing brief descriptions of what all the methods in each class are and what they do. Javadocs also include information about the inheritance structure of classes and much, much more… The Javadocs will be the most thorough resource you have for information about Java libraries. They can also be a little overwhelming because they contain so much information. In this lab you will learn how to look at the Javadocs to answer your questions. You can find a convenient link to them on the CS15 website (Documents » Java Documentation >> Javadocs), or choose the first result when you Google “Java 8 API”. A window that looks like this will open: There are three parts to the Javadocs: 1. Package List: Lists all the available packages (for example: java.lang ) that are a part of the API. 2. Class List: Lists all the classes contained within a particular package. For example, if you select java.lang from the package list, you would find the String class listed here among many others. 3. Class Details: When you click on a class from the class list it will display all information specific to that class. Hint: Clicking No Frames at the top will make the Package and Class list disappear. Click Frames to bring it back. Class Detail will probably be the most useful part of the Javadocs. Here is a small overview of what you will find using the Color class, which you used in this lab. The header shows the inheritance hierarchy for a given class, as well as any implemented interfaces and known subclasses (if applicable). Constructor Summary describes the different options for a constructor which can be used when an instance of the class is instantiated. (Objects can have multiple constructors that take in different parameters). Method Summary describes all of the methods that the class has, as well as all of the methods that the class has inherited from superclasses. Hint: If you Google a class that you need (for example, JavaFX Color), the relevant JavaFX document will be the first or second result. Learning about Javadocs ● Open up a blank text file in Atom (type ‘atom & ’ in a shell). ● Navigate to the Javadocs (you can find it from the CS15 Documents page). ● In your Atom document, answer the following questions: a. Name four subclasses of the javafx.scene.paint.Paint class. (Javadocs for Paint here: https://docs.oracle.com/javafx/2/api/javafx/scene/paint/Paint.html) b. List all the boolean return type methods of the class LinearGradient. c. What method would you call to invert a Color ( javafx.scene.paint.Color) ? d. What interfaces does String implement? Check Point 3: Grab a TA and get checked off. You are done with lab 3!