Question
Summary Inheritance and the Shape Hierarchy CSS 162: Programming Methodology Build three classes that inherit from an existing Shape superclass. These subclasses should all have
Summary
Inheritance and the Shape Hierarchy CSS 162: Programming Methodology
Build three classes that inherit from an existing Shape superclass. These subclasses should all have the phrase extends Shape in their class signatures, as in: public class MyNewShape extends Shape {. Your classes can and should be tested in isolation before combining your classes with the final drivers, so consider building a main in each subclass that tests out just that class. When you are confident in your subclasses, you can use this driver to see your shapes render to a JPanel. Note that this is also one of the drivers I will use when grading your work, so you should be sure to verify that these classes compile and execute before submitting.
Introduction
In the previous assignments, we were practicing new and review concepts. This assignment is the first in which well explore the idea of inheritance, which is a critical concept in object-oriented programming. Inheritance (as defined in the software realm) borrows from its Mendelian genetics: just as you can inherit your mothers eyes or your fathers sense of humor, one child class can inherit characteristics and behaviours of a parent class. More specifically to Java classes, when one child class inherits from a parent class (or a subclass inherits from a superclass), this child class gets a copy of all the methods and data from the parent class you can envision this as a copy-and-paste operation, from the parent to the child. Once the child class extends the parent class, any methods or data items found in the parent class are now a part of the child class, and the child class is free to define extra features. In Venn diagram terms, the parent class is strictly a subset of the child class, and would look like:
Child Class (Circle) Parent Class (Shape)
What this picture highlights is the relationship between parent classes and child classes everything the parent class has, the child also has. More to the point: any public method defined in the parent will also be defined in the child class, and this acts as a type of contract or guarantee. Indeed, it is a class invariant that the interface for a child class will contain every (public) method defined in the parent class interface (except for constructors). If we can promise that a Circle class has every method found in the Shape class, then anywhere in software a Shape is called for, a Circle could be substituted (this concept is called substitutability and is related to polymorphism, but more on that later). If we have a function
that expects a Ball object, and we have a subclass object VolleyBall, then we can pass the VolleyBall to the method with no problems, because a VolleyBall is a Ball, just like a Circle is a Shape. Inheritance then defines this type of is a relationship, which is a one-way relation between two classes. Note that the relationship isnt like the biconditional operator in that, all VolleyBalls are definitely Balls, but not all Balls are guaranteed to be VolleyBalls (some could be BaseBalls or BasketBalls, for example). When describing inheritance relationships, well use an arrow to indicate this one-way characteristic. When building classes that are interrelated via inheritance, inheritance hierarchies naturally arise; these are simply tree structures that display the is a inheritance relationships between the classes in your software.
Before we talk about the methods your Class must provide (or more specifically, override), we should get to know our Parent class Shape. The methods and data for this class are outlined below, and the code is available for download via the website (and youll need it in the same directory as your child classes).
Data Members
int x; //all shapes have an x,y coordinate pair in Java2D o Should this be public? Private? int y; //and so well have the Parent class manage this data o What access modifier should we use here?
Color myColor; o Colors are immutable, and so will have no getters/setters and also will not suffer
from privacy leaks.
Method Members
Shape(int x, int y) //a constructor used to initialize the data members
double getArea(); //used to calculate the area of this shape
o For a Circle, use Math.PI * r * r, and for a Square, side * side. void draw(Graphics g); //this method is called on each shape to draw itself
o See the Spray subclass for a sample implementation of the draw function o In some other Shape classes, this method is called paint. The only difference is the
name.
getX() //accessor
getY() //accessor
setX(int) //mutator
setY(int) //mutator
As you can see from above, the class is quite small. Now take a look in the code to see how many of the Shape functions are actually empty! Shape is really too generic of a class to offer an implementation for draw() or getArea(), as these are custom to specific subclasses of shape in fact, its our job to provide versions of these methods in our subclass that actually do draw shapes or calculate areas. We could easily have made Shape into an abstract class or even an interface, but for this demonstration well stick with a basic, mutable, nonstatic class. Your class will start with the line public Circle extends Shape, and this will only compile if Shape.java is also in the same working directory (or project) when compiling. As a Circle, you will need to add more data and methods that are custom to being a Circle; these are specifics that not just any shape would have, such as a radius data item and a getRadius() accessor function.
Circle Extends Shape
The first thing we need to do is download the Shape superclass, the Spray subclass, and the driver PolyDemo.java. This should compile and run with no modifications, but will only display a set of spray shapes on the screen (which are ovals that randomly change their width and height). You will create two subclasses of Shape (just like Spray) in the following steps.
(1) Downloadallthefilesandputthemintooneproject.
(2) RunthePolyDemo.javaandobservetheoutput.
(3) BuildanewclasscalledCircleusingtheinheritancekeywordextends
a. public class Circle extends Shape (4) OverridethegetArea()method
a. This method should return a double corresponding to the area of your shape. (5) Overridethedraw()method
a. This method will draw the shape onto the Graphics context g (or g2D). i. Look at Spray for an example of how to do this, or try: 1. g.draw3DRect(x,y,width,height, raised)
2. g.drawOval(x,y,width,height)
(6) Next,definemembersthatarecustomtoCircles,suchas:
private double radius;
double getRadius();
void setRadius(double);
(7) ModifyingthePolyDemofunctiongetRandShape()sothatitcancreateandreturnobjectsof your new Circle class.
a. Do this by replacing one of the switch cases that state retVal = new Spray() with retVal= new Circle().
(8) RunthePolyDemo.javaandobserveyournewCirclesubclassalsobeingrenderedtothe screen.
By following the three steps above, you should be able to iteratively develop each Shape subclass and test it first in isolation (make a small main inside that class to test it out), and then in the larger system that will use your subclass to actually draw stuff to the screen (PolyDemo.java).
A Short Primer on Java2D
This is the part of the assignment that has the least constraints on it. You are to develop a shape subclass, and you can draw it however you want using Java2D. Java uses a Graphics2D object to render things into drawable areas in Javas windows (or JFrames). We wont need to go much further than a google search to see all the functions you can call on a Graphics2D object, but some of them are: {drawRect, drawRoundRect, drawFillRect, drawOval, drawString} You could make a Shape subclass that represents a letter, and to draw that letter, youd provide a line of code in your draw() method like g.drawString(...) to draw the individual character. If making ovals, use the drawOval function, and the same strategy for rectangles. While the text is a bit light on the subject of Graphics, it does cover some data on drawing ovals and arcs on page 1033, and also shows you how to specify colors on page 1045. DrawString is at 1051, and again, there are tons of examples online for Java2D graphics code, but the main idea is: you are given a graphics object, and you make calls on that object to produce graphics you can view on the screen. Note that we will walk over an example Square class in class before the assignment is due, and Ill demonstrate some of the things you can do with a graphics object. In eclipse, if you simply type g., then the list of graphics functions will be highlighted for you, and theres a ton, so dont get overwhelmed. If you like, you can just leave the draw function very minimal until you have a grasp on how your class inherits from Shape, and how it fits into the driver code.
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
PolyDemo.java
import java.util.*; import javax.swing.*; import java.awt.*;
/* * Class PolyDemo (is a JFrame) and PolyDemoPanel (is a JPanel) * * Author: Rob Nash */
class PolyDemo extends JFrame {
public PolyDemo() { getContentPane().add( new PolyDemoPanel() ); //just some windowing stuff that must happen for all Frames setSize( 300,300 ); setVisible( true ); setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); }
public static void main( String args[] ) { PolyDemo myApp = new PolyDemo(); }
//this is our first "inner" or internal class //the purpose of this class is solely to support the JFrame class above, and I don't want it reused in arbitrary contexts, so by nesting this class here //I can indicate the intent a bit more clearly that this class "goes with" the class above it //In general, each class is a separate entity that should be contained in a separate file public class PolyDemoPanel extends JPanel { Shape[] myShapes= new Shape[20];
public PolyDemoPanel() { //Shape a = new Shape( getRandInt(), getRandInt()); //Shape b = new Circle( getRandInt(), getRandInt(), getRandInt() );
//a = new Square(getRandInt(), getRandInt(), getRandInt(), getRandInt() );
//a = getRandShape();
//( (Circle) b ).getRadius();
/********************************************************************************************************************* * Code for populating our myShapes changes minimally when new classes are introduced (only in getRandShape()) *********************************************************************************************************************/ for( int i = 0; i < 20; i++ ) { myShapes[i] = getRandShape(); } }
/********************************************************************************************************************* * Code for drawing our shapes doesn't change at all! Since we intended to take advantage of polymorphism, we coded * this "in general" with respect to the superclass, and not specific to any subclass. *********************************************************************************************************************/ public void paint( Graphics g ) { super.paint(g); //don't remove - required for GUI widgets to draw correctly /************************ * Late Binding Demo ************************/ for( int i = 0; i < myShapes.length; i++ ){ //which draw method is invoked here? There are many forms of the method (polymorphic), so which is chosen? //Java has RTTI about every object, and it uses this info to choose the correct method to invoke! myShapes[i].draw( g ); } }
public int getRandInt() { return ( (int) ( Math.random() * 200 ) ); }
public Shape getRandShape() { Shape retVal = null; final int x = getRandInt(); final int y = getRandInt();
/******************************** * Polymorphic extensibility demo * *******************************/ switch( ( int )(Math.random() * 4) ) { case 0: retVal = new Spray( x,y );//new Square( x, y, getRandInt(), getRandInt() ); break; case 1: retVal = new Spray( x,y );//Cube( x, y, getRandInt(), getRandInt(), getRandInt() ); break; case 2: retVal = new Spray( x,y ); break; case 3: retVal = new Spray( x,y );//new Circle( x,y,getRandInt() );////new Cylinder( x,y, getRandInt(), getRandInt() ); break; }
return retVal; } }
}
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Shape.java
import java.awt.*;
/* Class Shape * * By Rob Nash * * This is the superclass in a hierarchy of shapes that you have to construct */
//the superclass in our inheritance hierarchy //all "common" features, functions and data should go here //for example, all shapes in Java2D have a x,y that declares their position //and many of the shapes exposed have a width and a height (but not all, so we didn't put width and height here) //note that this class is mostly empty, as there is no algorithm generic enough to guess an arbitrary shape's area (future subclasses must override getArea() to provide something reasonable) //also, the draw method is empty too, as we don't know what shape to draw here! (again, our subclasses will need to replace this method with one that actually draws things) class Shape extends Object { private int x = 0; private int y = 0;
public Shape( int a, int b ) { x=a; y=b; }
public double getArea(){ return -1; }
public void draw( Graphics g ){}
public int getX() { return x; } public int getY() { return y; } }
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Spray.java
import java.awt.*; import javax.swing.*; import java.awt.geom.*; /* * Class Spray * * Not exactly the most obvious choice, and this has no getArea() implementation! * This is so I didn't give away everything to this assignment. =) * * Author: Rob Nash */
class Spray extends Shape { private final int RADIUS = 20; private final int DENSITY = 10;
public Spray( int a, int b ) { super( a, b); //we explicitly call a superclass constructor that takes 2 ints }
public void draw( Graphics g) { Graphics2D g2d = (Graphics2D) g;
final int x = getX(); final int y = getY();
g2d.setColor( Color.GREEN ); g2d.setPaint( new GradientPaint( x, y, Color.GREEN, x + RADIUS/4, y + RADIUS/4, Color.BLACK, true) );
int xOffset = 0; int yOffset = 0; for( int i = 0; i < DENSITY; i++) { xOffset = (int) (Math.random()*RADIUS); yOffset = (int) (Math.random()*RADIUS); g2d.draw( new Ellipse2D.Double( x + xOffset, y + yOffset, x + xOffset+3, y + yOffset+3) ); }
}
}
Step by Step Solution
There are 3 Steps involved in it
Step: 1
Get Instant Access to Expert-Tailored Solutions
See step-by-step solutions with expert insights and AI powered tools for academic success
Step: 2
Step: 3
Ace Your Homework with AI
Get the answers you need in no time with our AI-driven, step-by-step assistance
Get Started