7: Inheritance and Polymorphism

What We Will Cover


Illuminations

Questions on Completed Assignments?

  • Nothing

Questions from last class?

7.1: Working With the Object Class

Objectives

At the end of the lesson the student will be able to:

  • Use methods of the Object class
  • Override methods of the Object superclass
  • Write code to cast objects up and down inheritance chains

7.1.1: Methods of the Object Class

  • Every class implicitly extends java.lang.Object
  • This makes the Object class the ancestor of all Java classes
    • Including all programmer-defined classes
  • Thus, every class you write has access to the fields and methods of the Object class
  • That means you need to know how to work with the Object class

Object Class Methods

  • Following is a summary of some of the methods of the Object class
  • Method Description
    clone() Returns a copy of this object as an Object object. You must implement the Cloneable interface to use this method. (Cover later)
    equals(Object obj) Returns true if this object refers to the same space in memory as another object. Otherwise, returns false even if the other object contains the same data.
    finalize() Called by the garbage collector on an object when garbage collection determines that there are no more references to the object.
    getClass() Returns a Class object that represents the class of this object.
    hashCode() Returns a hash code value for the object, which is supported for the benefit of hashtables.
    toString() Returns a String object containing the class name followed by an @ symbol and the memory location (in hexadecimal) for the object.

  • Probably the most used method is the toString method
  • Java implicitly calls this method when it needs a String representation of an object
  • System.out.println(Student);
  • When coding a class, you typically override the toString method
    • To provide more details about the object
  • Similarly, you typically override the equals method for your classes
  • One method you usually do not override is finalize
  • Method finalize is called just before an object is deleted
  • Since memory management is automatic, usually no need to override the finalize method

7.1.2: Determining an Object's Type

  • Many times you assign an object to be of type Object
  • For instance, when you use the equals() method to compare two objects
  • public boolean equals(Object obj)
  • Also, some methods, like clone(), return an Object type
  • protected Object clone()
  • To work with these methods, you often want to know the actual type of the object
  • Java has two main ways of determining this information:
    • instanceof operator
    • run-time type identification (Reflection API)

Using the instanceof Operator

  • The instanceof operator checks if a test object is in the inheritance hierarchy of a class type
  • Syntax:
  • testObject instanceof ClassName
  • We saw an example of the instanceof operator before:
  • Person ed = new Student("Ed", 10);
    System.out.println(ed instanceof Student);
    
  • instanceof returns true if the testObject is a descendant of any ClassName

Using Reflection

  • You can use reflection to determine the exact type of an object
  • When Java runs an application, it keeps track of all the objects it loads using a class named Class
  • For each object it loads, Java creates a Class object that has information about the object
  • The getClass() method of Object returns the run-time class of an object
  • public final Class getClass()
  • Thus, you can get the Class information about any object
  • Among other methods, the Class object has a method named getName() that returns the name of the object
  • You can use these methods to determine the exact type of an object
  • Class c = ed.getClass()
    System.out.println(c.getName());
    

More Information

7.1.3: Casting Objects

  • Once you know what type of object you have, you often need to cast objects to their actual type
  • This allows you to use the methods of the more specialized object
  • Following is the inheritance chain for Student
  • Java will implicitly cast an object up the inheritance chain (upcasting)
  • However, you must explicitly cast an object down the inheritance chain (downcasting)
Student ed = new Student("Ed", 1);
Object obj = ed;             // cast Student to Object
Student ed2 = (Student) obj; // cast Object to Student
  • Following shows how casting affects the methods you can call
Object obj = new Student("Ed", 1);
String studStr = obj.toString(); // OK
// String name = obj.getName();  // does not compile
Student ed = (Student) obj; // cast Object to Student
String name = ed.getName(); // OK
  • Once we cast obj to Student, we can use the getName() method
  • Note that if you try to cast to an incompatible type you will get a compiler error
  • String str = (String) ed; // compiler error
    

7.1.4: Overriding the toString() Method

  • Method toString() of Object returns a String with the class name and memory location of the object
    • Not usually the behavior you want
  • Many classes in the API override this method
  • You will often want to override this method for your own classes
  • Following is an example of overriding the toString method of the Person class
  • public String toString() {
        return "A Person named " + name;
    }
    
  • With this method in place, you get a more descriptive value for a Person
  • Person ed = new Person("Ed");
    System.out.println(ed);
    String str = "Person String: " + ed;
    System.out.println(str);
    
  • Java automatically calls the toString method for println and when concatenating an object with a string

7.1.5: Overriding the equals() Method

  • To test if two objects refer to the same location, you use the equals() method of the Object class
  • For example:
1
2
3
4
5
6
7
8
9
10
public class EqualsTestApp {
    public static void main(String[] args) {
        Person p1 = new Person("Ed");
        Person p2 = new Person("Ed");
        if (p1.equals(p2))
            System.out.println("true");
        else
            System.out.println("false");
    }
}
  • This is often not the behavior you want and then you will need to override the equals() method
  • When overriding the equals() method, you want to make sure that:
    1. It returns false if the argument is null
    2. p1.equals(null) == false;
    3. It returns true or false consistently if the objects are used multiple times without changing
    4. It returns true if an object is compared to itself
    5. p1.equals(p1) == true;
    6. It returns true only if an object and the argument of the equals method can be interchanged and still return true
    7. p1.equals(p2) == p2.equals(p1);
    8. For any three reference variables p1, p2 and p3, if p1.equals(p2) == true and p2.equals(p3) == true, then p1.equals(p3) == true.
  • This may look daunting but is quite straightforward
  • As an example, we build a correct equals() method for Person
  • public boolean equals(Object obj)
  • We want our Person to work if the passed reference is null:
  • if (obj == null) {
        return false;
    }
    
  • We also want our equals() to work for any kind of object reference
  • Thus, if the passed object is not the same kind, we return false
  • To test the exact type, we use the getClass() method:
  • if (getClass() != obj.getClass()) {
        return false;
    }
    
  • If our test object passes these cases, then we compare all the instance variables of our class for equality
  • To do this, we must first cast our object to the actual type and then make the comparisons
  • Person p = (Person) obj;
    if (name.equals(p.getName())) {
        return true;
    }
    return false;
    
  • Putting all this together we have:
  • public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        } else if (getClass() != obj.getClass()) {
            return false;
        } else { // now safe to cast
            Person p = (Person) obj;
            if (name.equals(p.getName())) {
                return true;
            }
        }
        return false;
    }
    

About hashCode()

  • Note that if you override equals(), then you should override the hashCode() method as well.
  • Otherwise, your code will not work correctly with the hash-based classes of the Java Collection like HashTable
  • A hashtable is a data structure that you will learn about later
  • When an object is inserted in a hashtable, the hashCode() method is called
  • The value returned by hashCode() determines where to insert the object in the table
  • For our Person class, we can use the hashCode() of the String object:
  • public int hashCode() {
        int result = 17;
        result = 37 * result + name.hashCode();
        return result;
    }
    
  • It seems appropriate to look at the hashCode() method in more depth when studying hashtables
  • If you want to look at it sooner, see: Methods Common to All Objects from the book Effective Java

7.1.6: Summary

  • Every class extends from Object
  • Thus you can use methods from Object in your own classes
  • You can cast an object up and down its inheritance chain
    • Will not lose any data stored in the object
  • You can determine the actual type of an Object reference using either the:
    • instanceof operator
    • getClass() method
  • You can override methods of the Object class to improve the behavior of your class
  • Object method toString() is almost always overridden in subclasses
    • Return a String describing your object data
  • Object method equals() is overridden when you do not like the default behavior

Exercise 7.1

Take one minute to prepare an answer the following question:

  1. Given the following code, what will be printed?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class InstanceTester {
    public static void main(String[] args) {
        B b = new C();
        A a = b;
        if (a instanceof A) System.out.println("A");
        if (a instanceof B) System.out.println("B");
        if (a instanceof C) System.out.println("C");
        if (a instanceof D) System.out.println("D");
    }
}

class A {}
class B extends A {}
class C extends B {}
class D extends C {}
  1. A will be printed
  2. B will be printed
  3. C will be printed
  4. D will be printed

7.2: Abstract Classes and Polymorphism

Objectives

At the end of the lesson the student will be able to:

  • Code abstract classes
  • Describe the elements of a polymorphic system
  • Generate polymorphic behavior in Java programs

7.2.1: Abstract Classes and Methods

  • An abstract class serves as a template for subclasses
  • Used when classes are too generic to define real objects
  • For example: Shape
  • You cannot draw a generic shape
  • Instead, you create subclasses that you draw
  • Following is an inheritance hierarchy for an abstract class
  • A subclass inherits all non-private variables and methods from an abstract class
  • Abstract classes provide common code for all their subclasses

Coding an Abstract Class

  • Any class can become abstract using the keyword abstract
  • It means that you cannot instantiate an object of the class
  • Instead, it is a superclass from which other classes may inherit
  • Often referred to as an abstract superclass
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public abstract class Shape {
    // regular instance variable declarations
    private int x1, x2, y1, y2;

    // regular constructor and method declarations
    public Shape() { }
    public Shape(int newX1, int newY1, int newX2,
            int newY2) {
        x1 = newX1;
        x2 = newX2;
        y1 = newY1;
        y2 = newY2;
    }
    public int getX1() { return x1; }
    public int getX2() { return x2; }
    public int getY1() { return y1; }
    public int getY2() { return y2; }

    // Abstract method declaration
    public abstract void draw();
}

Abstract Methods

  • An abstract class may or may not contain abstract methods
  • An abstract method is a method declaration without a method body
  • public abstract void draw();
    
  • Any abstract methods must be implemented in any non-abstract subclass
  • You must declare a class abstract if it contains an abstract method

7.2.2: Introduction to Polymorphism

  • Any object oriented language must implement three programming mechanisms: Polymorphism, Inheritance and Encapsulation (PIE)
    • We have already covered encapsulation and inheritance
    • Thus, polymorphism is the last piece of the PIE.

    Polymorphism: the ability to redefine methods for subclasses and decide which definition to use at runtime.

  • Polymorphism is important because it allow us to generalize program code

For Example

  • Suppose you are designing a graphics package
  • You have classes for several figures such as lines, rectangles and squares
  • Each figure is an object of a different class
  • In a well-designed program, all of these shapes would be subclasses of one superclass, call it Shape
  • The superclass has code common to all the subclasses
  • For example, it might have variables for x and y coordinates to locate the object on the screen
  • Also, it would have get and set methods for the variables
  • In addition, the Shape class has an abstract methods named draw() that is overridden in each subclass
  • Each subclass only implements the code it needs
    • Such as overriding the method to draw its shape on the screen

The Application Program

  • To finish our design, we need to have a way to draw all the shapes on the screen
  • We make this easy, we decide to store all our shapes in an array named shapes[]
  • private Shape shapes[];
  • We can store each object reference to our subclasses in the array
  • For instance:
  • shape[x] = new Line()
  • All we have to do now is call the draw() method of each Shape subclass
  • This is easily and elegantly done using a for loop:
  • for (int i = 0; i < shape.length; i++) {
        shapes[i].draw();
    }
    
  • Will this design work?

7.2.3: Implementing Polymorphism

  • It turns out that this design works out well in Java
  • Calling the correct drawing method of the subclass is done automatically
  • Here is a simple implementation:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
abstract class Shape {
    private int x1, x2, y1, y2;

    // regular constructor and method declarations
    public Shape() { }
    public Shape(int newX1, int newY1, int newX2,
            int newY2) {
        x1 = newX1;
        x2 = newX2;
        y1 = newY1;
        y2 = newY2;
    }
    public int getX1() { return x1; }
    public int getX2() { return x2; }
    public int getY1() { return y1; }
    public int getY2() { return y2; }

    // Abstract method declaration
    public abstract void draw();
}

class Line extends Shape {
    public void draw() {
        System.out.println("Drawing a Line.");
    }
}

class Rectangle extends Shape {
    public void draw() {
        System.out.println("Drawing a Rectangle.");
    }
}

class Square extends Rectangle {
    public void draw() {
        System.out.println("Drawing a Square.");
    }
}

class Oval extends Shape {
    public void draw() {
        System.out.println("Drawing an Oval.");
    }
}

public class DrawingApp {
    private Shape shapes[] = { new Line(),
            new Rectangle(), new Square(),
            new Oval() };

    public static void main(String args[]) {
        DrawingApp app = new DrawingApp();
        app.drawShapes();
    }

    public void drawShapes() {
        for (int i = 0; i < shapes.length; i++)
            shapes[i].draw();
    }
}
  • So why does this work?
  • Answer: dynamic binding

Static vs. Dynamic Binding

    Binding: determining which method to call based on its signature.

  • Static binding: done at compile time
  • Dynamic binding: done at run-time
  • Compilation binds memory locations to methods before running a program
  • Thus, binding done at compile time is called static, or early, binding
  • Binding done at run time is called dynamic, or late, binding
  • Java uses dynamic binding for all instance methods
  • In contrast, static methods are bound at compile time (static binding)
  • Thus, in Java, polymorphism relies on late binding

7.2.4: Making Changes

  • Let us suppose that our program is already written and in use
  • Now a customer wants us to add a new shape like a triangle
  • Now we need to create a Triangle class and add it to the program
  • We want to make minimal changes and so have Triangle subclass Shape
  • Do we need to change Shape to implement this new class?

Adding a Triangle

  • Here is the code for our Triangle class
  • class Triangle extends Shape {
        public void draw() {
            System.out.println("Drawing a Triangle.");
        }
    }
    
  • All need now is a way to add Triangle objects to the shapes array
  • For our simple application:
    private Shape shapes[] = { new Line(),
            new Rectangle(), new Square(),
            new Oval(), new Triangle() };
    
  • Note how easy it is to change a polymorphic program
  • One of the chief advantages of polymorphism is extensibility

7.2.5: Summary

  • Abstract classes serve as templates for subclasses
    • Provides common code for all the subclasses
  • Abstract classes often implement abstract methods
  • public abstract void draw();
  • Requires any subclass to implement the method
  • Polymorphism is a feature of inheritance that lets you treat subclasses as if they were a superclass type
  • Programs are written to use methods declared in the superclass
  • Superclass methods are overridden in subclasses
  • Objects of subclass types are created and stored as superclass references
  • private Shape shapes[];
  • The correct method is called automatically using a process known as dynamic binding
  • for (int i = 0; i < shape.length; i++) {
        shapes[i].draw();
    }
    
  • Polymorphism is used to make systems more easily extensible
    • Encourages object decoupling by reducing dependencies
    • Classes are easily added to the system

Exercise 7.2

Take two minutes to prepare an answer the following questions.

  1. What is the class hierarchy of the following Polymorphism code?
  2. What will be the result of attempting to compile and run the following code?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Polymorphism {
    public static void main(String[] args) {
        A ref1 = new C();
        B ref2 = (B) ref1;
        System.out.println(ref2.f());
    }
}

class A { int f() { return 1; } }
class B extends A {
    int f() { return 2; }
}
class C extends B {
    int f() { return 3; }
}

7.3: Simple Graphics

Objectives

At the end of the lesson the student will be able to:

  • Generate code to draw simple shapes
  • Control the color of drawing objects
  • Describe the process for creating Graphics2D objects

7.3.1: About Java's Graphics Capabilities

  • Java has always provided many graphics capabilities including
    • Graphical User Interfaces (GUIs)
    • Creation of arbitrary 2D shapes
    • Control of colors and fonts
  • Over the years, Java has added more graphics capabilities such as:
    • Displaying images, sounds and movies
    • Mechanisms for sophisticated 2D shape transformations
    • Creation and transformation of arbitrary 3D shapes
  • In this section we learn how to draw basic figures such as lines, ovals, and rectangles.
  • In addition, we will learn how to control colors and gain access to the Java 2D API
  • Java 2D API adds more sophisticated graphics capabilities
    • Drawing and transforming arbitrary 2D shapes
    • Various rendering techniques and effects
    • Image filtering and transformations

Java's Coordinate System

  • Java provides a coordinate system for identifying every point on the screen
  • Each point is a pixels and starts from the upper-left corner of screen
  • Any single point has an x-coordinate and y-coordinate
  • Coordinates are usually positive numbers

7.3.2: Accessing the Graphics Object

  • Java provides a Graphics object that lets you draw on the screen
    • Graphics is part of the java.awt package
  • Contains methods used to draw basic shapes and lines
  • Class Graphics is abstract and cannot be instantiated
  • To use this object, we have to gain access to it

Using JFrame and JPanel to Access Graphics

  • The easiest way to get access to the Graphics object is from a GUI component
  • We will look at GUI's in more detail later, but for now we want to make use of two GUI components
  • A JFrame provides a window structure that you can use for other GUI components
  • A JPanel provides a general-purpose container for GUI components
  • Both JFrame and JPanel are part of the javax.swing package
  • import javax.swing.*;
  • Typically, you extend a JPanel to create your "drawing palette"
  • public class DrawingPalette extends JPanel
  • Then, you add your drawing palette to a JFrame
  • JFrame frame = new JFrame("Drawing Palette");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    DrawingPalette panel = new DrawingPalette();
    panel.setBackground(Color.WHITE);
    frame.add(panel);
    frame.setSize(WIDTH, HEIGHT);
    frame.setVisible(true);
    
  • The JFrame then takes care of displaying your drawing palette
  • One of the methods of a JPanel is paintComponent(Graphics g)
  • To use the Graphics object, we override the paintComponent() method
  • public void paintComponent(Graphics g) {
        super.paintComponent(g);
        // start using Graphics g
    }
    
  • Note that the paintComponent() method is called automatically
    • Should not be invoked in the programmer's code.
    • To schedule paintComponent(), call method repaint()
  • With all this, we have a template for accessing the Graphics object:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import java.awt.*;
import javax.swing.*;

public class DrawingPalette extends JPanel {
    public static int HEIGHT = 200;
    public static int WIDTH = 400;

    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        // start using Graphics g
    }
    public static void main(String args[]) {
        JFrame frame = new JFrame("Drawing Palette");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        DrawingPalette panel = new DrawingPalette();
        panel.setBackground(Color.WHITE);
        frame.add(panel);
        frame.setSize(WIDTH, HEIGHT);
        frame.setVisible(true);
    }
}

Components and Methods Used to Access the Graphics Object

Component/Method Description
JFrame A GUI component with a title, border and buttons for closing and iconifying the window.
JPanel Provides a general-purpose container that can be used as a drawing palette.
paintComponent(Graphics g) Automatically called when a component needs to be painted.

7.3.3: Drawing Simple Shapes

  • Now that we can access the Graphics object we can draw shapes
  • Most drawing methods have arguments for width and height
  • Most methods for painting shapes have two versions:
    • Draw version which only draws the outline of the shape (e.g. drawOval)
    • Fill version which fills in the shape (e.g. fillOval)
  • Some of the methods that draw and fill shapes are shown below with links to their API documentation

Some Graphics Methods that Draw Shapes

drawLine(int x1, int y1, int x2, int y2)
drawRect(int x, int y, int width, int height)
drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight)
drawOval(int x, int y, int width, int height)
drawArc(int x, int y, int width, int height, int startAngle, int arcAngle)

Some Graphics Methods that Fill Shapes

fillRect(int x, int y, int width, int height)
fill3DRect(int x, int y, int width, int height, boolean raised)
fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight)
fillOval(int x, int y, int width, int height)
fillArc(int x, int y, int width, int height, int startAngle, int arcAngle)

For Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import java.awt.*;
import javax.swing.*;

public class ShapesPanel extends JPanel {
    public static int HEIGHT = 200;
    public static int WIDTH = 400;

    public void paintComponent(Graphics g) {
        super.paintComponent(g);

        g.drawLine(5, 30, 350, 30);

        g.drawRect(5, 40, 90, 55);
        g.fillRect(100, 40, 90, 55);

        g.fillRoundRect(195, 40, 90, 55, 50, 50);
        g.drawRoundRect(290, 40, 90, 55, 20, 20);

        g.draw3DRect(5, 100, 90, 55, true);
        g.fill3DRect(100, 100, 90, 55, false);

        g.drawOval(195, 100, 90, 55);
        g.fillOval(290, 100, 90, 55);
    }

    public static void main(String args[]) {
        JFrame frame = new JFrame("Drawing Palette");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        ShapesPanel panel = new ShapesPanel();
        panel.setBackground(Color.WHITE);
        frame.add(panel);
        frame.setSize(WIDTH, HEIGHT);
        frame.setVisible(true);
    }
}

Drawing Arcs

  • Arcs are just a portion of an oval
  • Specify the beginning angle and the degrees of the sweep
  • g.drawArc(x, y, width, height, startAngle, arcAngle);
    
  • Sweep starts at the specified starting angle
  • Sweeps through the number of degrees specified in the arc angle
  • Counterclockwise sweep is measure in positive degrees
  • Clockwise sweep is measure in negative degrees

7.3.4: Controlling Colors

  • Class Color defines the methods and constants for manipulating colors
  • Colors are created from red, green and blue (RGB) values
  • For example:
  • Color brown = new Color(204, 102, 0);
    
  • RGB values can be specified as type float (0.0 to 1.0) or type int (0 to 255)
  • Color(float r, float g, float b) // 0.0 - 1.0
    Color(int r, int g, int b)       // 0 - 255
    
  • The Color class has several define color constants that you can use in place of numbers
  • Color.red
  • To set a color, use the setColor() method
  • g.setColor(Color.red);
  • After you set a color, all shapes are drawn in that color
  • To change the color, you must set a new color

For Example

  • Here is a repeat of the ShapePanel with colors added:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import java.awt.*;
import javax.swing.*;

public class ShapesPanel2 extends JPanel {
    public static int HEIGHT = 200;
    public static int WIDTH = 400;

    public void paintComponent(Graphics g) {
        super.paintComponent(g);

        g.setColor(Color.RED);
        g.drawLine(5, 30, 350, 30);

        g.setColor(Color.BLUE);
        g.drawRect(5, 40, 90, 55);
        g.fillRect(100, 40, 90, 55);

        g.setColor(Color.CYAN);
        g.fillRoundRect(195, 40, 90, 55, 50, 50);
        g.drawRoundRect(290, 40, 90, 55, 20, 20);

        g.setColor(Color.YELLOW);
        g.draw3DRect(5, 100, 90, 55, true);
        g.fill3DRect(100, 100, 90, 55, false);

        g.setColor(Color.MAGENTA);
        g.drawOval(195, 100, 90, 55);
        g.fillOval(290, 100, 90, 55);
    }

    public static void main(String args[]) {
        JFrame frame = new JFrame("Drawing Palette");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        ShapesPanel2 panel = new ShapesPanel2();
        panel.setBackground(Color.WHITE);
        frame.add(panel);
        frame.setSize(WIDTH, HEIGHT);
        frame.setVisible(true);
    }
}

7.3.5: Extending to Graphics2D

  • Java 2D API provides advanced 2D graphics capabilities
    • Draw lines of any thickness
    • Fill shapes with gradients and textures
    • Move, rotate, scale, and shear text and graphics
    • Composite overlapping text and graphics
  • Drawing needs an instance of Graphics2D
  • Graphics2D is subclass of Graphics
  • To access Graphics2D in paintComponent(), you need to downcast from Graphics
  • public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D)g;
        // do something with g2d
    }
    
  • Java 2D uses shapes from the java.awt.geom package
    • Ellipse2D.Double
    • Rectangle2D.Double
    • RoundRectangle2D.Double
    • Arc3D.Double
    • Lines2D.Double
  • Note that Double is a static inner class of the shape
    • Will discuss inner classes later in course
  • Following is adapted for example 12.29 from the textbook and demonstrates drawing with Java2D shapes
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
import java.awt.*;
import java.awt.geom.*;
import java.awt.image.*;
import javax.swing.*;

public class ShapesPanel2D extends JPanel {
    public static int HEIGHT = 200;
    public static int WIDTH = 425;

    public void paintComponent(Graphics g) {
        super.paintComponent(g);

        Graphics2D g2d = (Graphics2D) g;

        // 2D ellipse filled with blue-yellow gradient
        g2d.setPaint(new GradientPaint(5, 30,
            Color.BLUE, 35, 100, Color.YELLOW, true));
        g2d.fill(new Ellipse2D.Double(5, 30, 65, 100));

        // draw 2D rectangle in red
        g2d.setPaint(Color.RED);
        g2d.setStroke(new BasicStroke(10.0f));
        g2d.draw(new Rectangle2D.Double(80, 30, 65, 100));

        // 2D rounded rectangle with buffered background
        BufferedImage buffImage = new BufferedImage(10, 10,
            BufferedImage.TYPE_INT_RGB);

        // obtain Graphics2D from bufferImage and draw
        Graphics2D gg = buffImage.createGraphics();
        gg.setColor(Color.YELLOW); // draw in yellow
        gg.fillRect(0, 0, 10, 10); // filled rectangle
        gg.setColor(Color.BLACK);  // draw in black
        gg.drawRect(1, 1, 6, 6);   // draw rectangle
        gg.setColor(Color.BLUE);   // draw in blue
        gg.fillRect(1, 1, 3, 3); // draw filled rectangle
        gg.setColor(Color.RED);  // draw in red
        gg.fillRect(4, 4, 3, 3); // draw filled rectangle

        // paint buffImage onto the JFrame
        g2d.setPaint(new TexturePaint(buffImage,
            new Rectangle(10, 10)));
        g2d.fill(new RoundRectangle2D.Double(155, 30, 75,
            100, 50, 50));

        // draw 2D pie-shaped arc in white
        g2d.setPaint(Color.WHITE);
        g2d.setStroke(new BasicStroke(6.0f));
        g2d.draw(new Arc2D.Double(240, 30, 75, 100, 0,
            270, Arc2D.PIE));

        // draw 2D lines in green and yellow
        g2d.setPaint(Color.GREEN);
        g2d.draw(new Line2D.Double(395, 30, 320, 150));

        // draw 2D line using stroke
        float dashes[] = { 10 }; // specify dash pattern
        g2d.setPaint(Color.YELLOW);
        g2d.setStroke(new BasicStroke(4,
            BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND,
            10, dashes, 0));
        g2d.draw(new Line2D.Double(320, 30, 395, 150));
    }

    public static void main(String args[]) {
        JFrame frame = new JFrame("Drawing Palette");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        ShapesPanel2D panel = new ShapesPanel2D();
        panel.setBackground(Color.WHITE);
        frame.add(panel);
        frame.setSize(WIDTH, HEIGHT);
        frame.setVisible(true);
    }
}

More Information

7.3.6: Summary

  • Java provides many graphical capabilities
  • In this section we looked at how to draw simple shapes and use colors
  • Drawing shapes and setting colors is done with the Graphics object
    • Drawing starts in the upper left-hand corner of the screen
  • The easiest way to get access to the Graphics object is from a GUI component
  • We looked at how to set up a drawing palette for using the Graphics object:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import java.awt.*;
import javax.swing.*;

public class DrawingPalette extends JPanel {
    public static int HEIGHT = 200;
    public static int WIDTH = 400;

    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        // start using Graphics g
    }
    public static void main(String args[]) {
        JFrame frame = new JFrame("Drawing Palette");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        DrawingPalette panel = new DrawingPalette();
        panel.setBackground(Color.WHITE);
        frame.add(panel);
        frame.setSize(WIDTH, HEIGHT);
        frame.setVisible(true);
    }
}
  • We used the methods of the Graphics object to draw a variety of shapes
  • g.drawLine(5, 30, 350, 30);
    g.drawRect(5, 40, 90, 55);
    g.fillRect(100, 40, 90, 55);
    g.fillRoundRect(195, 40, 90, 55, 50, 50);
    g.drawRoundRect(290, 40, 90, 55, 20, 20);
    g.draw3DRect(5, 100, 90, 55, true);
    g.fill3DRect(100, 100, 90, 55, false);
    g.drawOval(195, 100, 90, 55);
    g.fillOval(290, 100, 90, 55);
    
  • Also, we used the Color object to set colors for the shapes we drew
  • g.setColor(Color.red);
  • In addition, we saw that you can downcast the Graphics object to a Graphics2D object
  • public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D)g;
        // do something with g2d
    }
    
  • Graphics2D provides many more capabilities than Graphics

Exercise 7.3

Take one minute to prepare an answers to the following questions:

  1. Where is the origin point of the graphics screen?
  2. What data types does the constructor of the Color class take?
  3. What is the size of the area affected by the following code?
  4. public void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.fillRect(10, 10, 30, 30);
    }
    
    1. 19 x 19 pixels
    2. 20 x 20 pixels
    3. 21 x 21 pixels
    4. 29 x 29 pixels
    5. 30 x 30 pixels
    6. 31 x 31 pixels

Wrap Up

Home | WebCT | Announcements | Schedule | Room Policies | Course Info
Help | FAQ's | HowTo's | Links

Last Updated: April 16 2005 @16:58:55