0

I am supposed to modify a program to present output in a GUI. This is mostly done, except for one field. The only field that I am unable to display is the 'restocking fee', which is in a subclass of DVD. Inventory is the main class, then DVD class and MovieTitle is the subclass of DVD. Everything displays correctly except the restockingFee in the MovieTitle subclass. The exception is:

Exception in thread "main" java.lang.ClassCastException: inventoryProgram3.DVD cannot be cast to inventoryProgram3.MovieTitle
    at inventoryProgram3.Inventory.myRedraw(Inventory.java:204)
    at inventoryProgram3.Inventory.<init>(Inventory.java:149)
    at inventoryProgram3.Inventory.main(Inventory.java:156)  

How do I call the restockfee method from the subclass?

This is the Inventory class:

package inventoryProgram4;

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;

@SuppressWarnings("serial")
public class Inventory extends JFrame implements ActionListener {
    // declare instance attributes
    private final JPanel buttonJPanel;
    private final JPanel areaJPanel;
    private final JButton buttons[];
    private final BorderLayout layout;
    private final Container pane;
    private final JLabel l0;
    private final JLabel l1;
    private final JLabel l2;
    private final JLabel l3;

    private final JLabel l4;
    private final JLabel l5;
    private final JLabel totalInv;

    // Text fields
    static private JTextField t0;
    static private JTextField t1;
    static private JTextField t2;
    static private JTextField t3;
    static private JTextField t4;
    static private JTextField t5;

    static private JTextField totalInvValue;
    static private DVD rb[];
    static private int index;

    static private DVD[] DVD;

    public Inventory() { // constructor
        super("DVD Inventory");

        DVD = new DVD[5];
        DVD[0] = new DVD("1", " Fast and Furious 6", 10, 15);
        DVD[1] = new DVD("2", " The Matrix Reloaded", 8, 5);
        DVD[2] = new DVD("3", " In Pursuit of Happiness", 8, 5);
        DVD[3] = new DVD("4", " Darknet", 8, 5);
        DVD[4] = new DVD("5", " Goonies", 8, 5);

        DVD = sortDVD(DVD);

        rb = DVD;
        index = 0;

        // setting layout
        layout = new BorderLayout(25, 15);
        setLayout(layout);

        // getting pane
        pane = getContentPane();

        // set up areaJPanel
        areaJPanel = new JPanel();
        areaJPanel.setLayout(new GridLayout(0, 2));

        // create area components
        l0 = new JLabel("   DVD Title:   ");
        l1 = new JLabel("   Title Name: ");
        l2 = new JLabel("   DVD's in Stock:    ");
        l3 = new JLabel("   DVD Price:  ");

        l4 = new JLabel("   Restock Fee: ");
        l5 = new JLabel("   Inventory value: ");
        totalInv = new JLabel("   Value of entire inventory: ");
        totalInvValue = new JTextField(10);
        totalInvValue.setEditable(false);

        t0 = new JTextField(15);
        t0.setEditable(false); // making text box non editable
        t1 = new JTextField(15);
        t1.setEditable(false);
        t2 = new JTextField(10);
        t2.setEditable(false);
        t3 = new JTextField(10);
        t3.setEditable(false);
        t4 = new JTextField(10);
        t4.setEditable(false);
        t5 = new JTextField(10);
        t5.setEditable(false);

        // adding components
        areaJPanel.add(l0); // n x 2 grid: label - textfield
        areaJPanel.add(t0);
        areaJPanel.add(l1);
        areaJPanel.add(t1);
        areaJPanel.add(l2);
        areaJPanel.add(t2);
        areaJPanel.add(l3);
        areaJPanel.add(t3);

        areaJPanel.add(t4);
        areaJPanel.add(l4);
        areaJPanel.add(t4);
        areaJPanel.add(l5);
        areaJPanel.add(t5);
        areaJPanel.add(totalInv);
        areaJPanel.add(totalInvValue);

        // Adding four buttons: first, next, previous, last
        // create array of buttons, size 4
        buttons = new JButton[4];

        // create a JPanel for the buttons
        buttonJPanel = new JPanel();
        // set panel layout to 1 row and columns = size of array
        buttonJPanel.setLayout(new GridLayout(1, buttons.length));

        // adding button "next"
        buttons[1] = new JButton("Previous"); // instantiate button "next"
        buttons[1].setActionCommand("Previous"); // identifies the action event
        buttons[1].addActionListener(this); // add an action listener for button
        buttonJPanel.add(buttons[1]); // add button to the button JPanel

        // adding button "previous"
        buttons[2] = new JButton("Next"); // instantiate button "previous"
        buttons[2].setActionCommand("next"); // identifies the action event
        buttons[2].addActionListener(this); // add an action listener for button
        buttonJPanel.add(buttons[2]); // add button to the button JPanel

        pane.add(buttonJPanel, BorderLayout.SOUTH); // place buttons at bottom of pane
        pane.add(areaJPanel, BorderLayout.WEST);

        myRedraw(); // showing first item

    }// end constructor

    public static void main(String args[]) {

        // creating frame
        Inventory panelFrame = new Inventory();
        // close the frame on clicking X button
        panelFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        // setting size
        panelFrame.pack();
        // setting visibility
        panelFrame.setVisible(true);

    }// end main method

    static DVD[] sortDVD(DVD[] DVD) {
        int in, out, NumOfEle = DVD.length;

        for (out = 1; out < NumOfEle; out++) {
            DVD temp = DVD[out];
            in = out;

            while (in > 0 && DVD[in - 1].getTitle().compareTo(temp.getTitle()) > 0) {
                DVD[in] = DVD[in - 1];
                --in;
            }
            DVD[in] = temp;
        }
        return DVD;
    }

    private static double calculateEntireInventory(DVD[] DVD1) {// method to return inventory total
        double totalInventory = 0;
        for (DVD DVD : DVD1) {
            totalInventory += (DVD.getInventoryValue());
        }
        return totalInventory;
    }

    // method to display contents
    static void myRedraw() {
        // currency formatter
        NumberFormat nf = new DecimalFormat("$#,##0.00");
        // reference to object under consideration
        DVD f = (DVD) rb[index];
        double inventoryValue = f.getInventoryValue();
        // set values for indicated object attribute
        t0.setText(f.getTitle()); // item name
        t1.setText(f.getItem() + ""); // item number
        t2.setText(f.getStock() + ""); // item quantity
        t3.setText(nf.format(f.getPrice())); // item price

        // Problem here
        /**
         * Exception in thread "main" java.lang.ClassCastException: inventoryProgram3.DVD cannot be
         * cast to inventoryProgram3.MovieTitle at
         * inventoryProgram3.Inventory.myRedraw(Inventory.java:204) at
         * inventoryProgram3.Inventory.<init>(Inventory.java:149) at
         * inventoryProgram3.Inventory.main(Inventory.java:156)
         **/

        MovieTitle t = (MovieTitle) f;

        t4.setText(nf.format(t.getRestockingFee())); // item re-stocking fee

        // up above

        t5.setText(nf.format(inventoryValue));

        // up above

        // total value of inventory
        totalInvValue.setText(nf.format(calculateEntireInventory(DVD)));
    }// end method

    // action handler for button presses
    public void actionPerformed(ActionEvent event) {
        int last = rb.length - 1;

        // get command string assigned to the pressed button.
        String action = event.getActionCommand();

        // set index based on which button was pressed.
        if (action.equals("prev")) { // previous pressed
            if (index == 0) {
                index = last; // at first item, go to last item
            } else {
                --index; // go to previous item
            }
        } else {// next pressed
            if (index == last) {
                index = 0; // at last item, go to first time
            } else {
                ++index; // go to next item
            }
        }
        myRedraw();

    }// end method

} // end program

This is the DVD class:

package inventoryProgram3;

class DVD {
    // Create local variable to capture data from parameter list of the constructor

    private String thisItem;
    private String thisTitle;
    private double thisStock;
    private double thisPrice;

    // Create constructor to call this class from external classes
    public DVD(String item, String title, double stock, double price) {
        // Set local variables to values passed in via a class call
        thisItem = item;
        thisTitle = title;
        thisStock = stock;
        thisPrice = price;
    } // end constructor

    // getInventoryValue: calculates the total value of the inventory for the item
    public double getInventoryValue() {
        return getPrice() * getStock();
    }

    // set name
    public void setTitle(String title) {
        thisTitle = title;
    }// end method setTitle

    // return Title
    public String getTitle() {
        return thisTitle;
    }// end method getTitle

    // set Stock
    public void setStock(double stock) {
        thisStock = stock;
    }// end method setStock

    // return Stock
    public double getStock() {
        return thisStock;
    }// end method get Stock

    public void setPrice(double price) {
        thisPrice = price;
    }// end method setPrice

    // return Price
    public double getPrice() {
        return thisPrice;
    }// end method getPrice

    public void setItem(String item) {
        thisItem = item;
    }// end method setItem

    // return Item
    public String getItem() {
        return thisItem;
    }// end method getItem

    // calculate the inventory value
    public double value() {
        return thisPrice * thisStock;
    }// end method value
}// end class

This is the MovieTitle class:

package inventoryProgram3;

public class MovieTitle extends DVD {
    private final double restockingFee = 0.05;

    public MovieTitle(String item, String title, double stock, double price) {
        super(item, title, stock, price);
    }

    public double getRestockingFee() {
        return getPrice() * restockingFee;
    }
}
Tom
  • 16,842
  • 17
  • 45
  • 54
James
  • 69
  • 7
  • possible duplicate of [explicit casting from super class to subclass](http://stackoverflow.com/questions/4862960/explicit-casting-from-super-class-to-subclass) – Madhan Jun 27 '15 at 16:55

5 Answers5

3

You cannot cast a superclass object to one of its children, only the reverse is allowed. It looks the simplest way to fix what you currently have is to declare the array as of type child and populate it with child objects in the main program and then you can access both the subclass and the superclass methods.

Ron Thompson
  • 1,086
  • 6
  • 12
3

The exception indicates what is wrong. You are trying to cast a DVD to a MovieTitle. Try drawing yourself a venn diagram. All MovieTitles are DVDs but not all DVDs are MovieTitles. The exception indicates that you are trying to handle one of those DVDs-that-are-not-movie-titles as if it were a MovieTitle.

An initial guess would be that you instantiated a DVD directly. You can confirm this by reading your code to see where you instantiate the object. Either you need to instantiate the object as a MovieTitle instead of a DVD or you need to use a method of DVD to obtain the title of the DVD.

atk
  • 9,244
  • 3
  • 32
  • 32
0

The error is shown because you are casting the DVD to a MovieTitle. A DVD is not a MovieTtitle object so you can't cast it like that.

The restockFee is a more specific Price for a MovieTitle.

Try this and see if it works:

In the MovieTitle class:

@Override
public double getPrice { 
    return super.getPrice() * restockingFee;
}

In the Inventory Class:

//MovieTitle t = (MovieTitle) f;
//t4.setText(nf.format(t.getRestockingFee())); // item re-stocking fee
t4.setText(nf.format(f.getPrice()); // item re-stocking fee
  • Thank you everyone, I do not quite have it figured out, but I am working on it. The suggestions here have helped alot. – James Jun 27 '15 at 18:17
0

You should adopt a naming convention, code like DVD = new DVD[5] is very difficult to read.

Regarding your question, casting a superclass instance to its subclass would not make sense.

Let's suppose that the cast succeeds. Since the superclass does not have the state that is defined in the subclass, what would be the outcome when invoking a subclass method that works with fields defined in the subclass? Those fields do not exist in the superclass object on which you performed the cast. In your example, there is no restockingFee in the DVD instance, so what would you get?

In languages in which this is allowed, like C family of languages, you would get whatever memory content is immediately after the boundary of the DVD object (in other words you would have a bug). Java protects you from this bug and throws ClassCastException.

There are a number of possible solutions to your issue:

1) Move the state/methods you need to superclass. In your example, if you always use restockingFee on DVDs, move it to DVD class.

2) If restockingFee makes sense only in the subclass (seems to be your use case), and you use both superclass and subclass instances, then you need to read it only when you work with the subclass instances:

if (f instanceof MovieTitle) {
   MovieTitle t = (MovieTitle) f;
   t4.setText(nf.format(t.getRestockingFee()));
}

3) If you work only with subclass instances, then declare the relevant fields to be of subclass type and avoid the cast entirely (you get compile time safety this way, avoiding runtime errors you are facing):

static private MovieTitle rb[];

4) General solution for more complex designs when you work with incompatible interfaces is the Adapter pattern.

Dragan Bozanovic
  • 23,102
  • 5
  • 43
  • 110
  • I am still getting use to the naming conventions, I am almost done with my first Java class (semester), still lots to learn, in which I do plan on it in what little free time I do have. As for putting the subclass method in the superclass, I would love to to make it easier on me, but that is not the assignment. – James Jun 29 '15 at 17:40
0

I finally found the answer, even though I do not really understand it, I was looking at other code and tried different things. My original code in main class with the array:

    public Inventory() { // constructor
    super("DVD1");    
    DVD = new DVD[7];
    DVD[0] = new DVD ( "1", " Fast and Furious 6", 10, 15 );
    DVD[1] = new DVD ( "2", " The Matrix Reloaded", 8, 5 );
    DVD[2] = new DVD ( "3", " In Pursuit of Happiness", 8, 5 );
    DVD[3] = new DVD ( "4", " Darknet", 8, 5 );
    DVD[4] = new DVD ( "5", " Goonies", 8, 5 );

Is now changed to and works is:

     public Inventory() { // constructor
     super("DVD1");
     DVD = new DVD[7];
     DVD[0] = new MovieTitle ( "1", " Fast and Furious 6", 10, 15 );
     DVD[1] = new MovieTitle ( "2", " The Matrix Reloaded", 8, 5 );
     DVD[2] = new MovieTitle ( "3", " In Pursuit of Happiness", 8, 5 );
     DVD[3] = new MovieTitle ( "4", " Darknet", 8, 5 );
     DVD[4] = new MovieTitle ( "5", " Goonies", 8, 5 );
     DVD[5] = new MovieTitle ( "6", " zzzz", 8, 5 );
     DVD[6] = new MovieTitle ( "7", " AAaaaa", 15, 19.99 );
 </code>

I added in the last two to the array and added two more buttons (first and last) to make sure the buttons worked. With changing the new DVD to new MovieTitle, the restockFee does show up in GUI. Yes, I also needed:

if (f instanceof MovieTitle) { MovieTitle t = (MovieTitle) f;
t4.setText(nf.format(t.getRestockFee())); // item re-stocking fee }

James
  • 69
  • 7