Programming lesson
Building an Invoice Class in Java: A Step-by-Step Tutorial for CSCI 470/502
Learn how to create a Java Invoice class with instance variables, constructors, getters/setters, and a getInvoiceAmount method. Includes a complete InvoiceTest app with five hardware store items and formatted output using DecimalFormat.
Introduction to Java Classes and Objects
In this tutorial, we'll build a practical Java class called Invoice that models a hardware store invoice. This assignment is common in courses like CSCI 470/502 and focuses on fundamental OOP concepts: instance variables, constructors, getters and setters, and utility methods. By the end, you'll have a reusable class and a test application that prints formatted invoices.
Think of a class as a blueprint for creating objects. For example, in the world of viral apps like TikTok, each user profile is an object of a User class. Similarly, our Invoice class will define what an invoice looks like—its parts, description, quantity, and price—and then we'll create multiple invoice objects representing real hardware items.
Step 1: Define the Invoice Class
Create a new Java file named Invoice.java. Start with the documentation box and class declaration.
/**
* Invoice class represents an invoice for an item sold at a hardware store.
* @author Your Name
* @version 1.0
*/
public class Invoice {
// instance variables
private String partNumber;
private String partDescription;
private int quantity;
private double pricePerItem;
The four instance variables store the core data. We make them private to enforce encapsulation—data is hidden and only accessible via public methods.
Step 2: Add Constructors
We need two constructors: a no-argument constructor and a parameterized constructor. The no-argument constructor sets default values. The parameterized constructor initializes all fields using setter methods to enforce validation.
// No-argument constructor
public Invoice() {
this.partNumber = "";
this.partDescription = "";
this.quantity = 0;
this.pricePerItem = 0.0;
}
// Parameterized constructor
public Invoice(String partNumber, String partDescription, int quantity, double pricePerItem) {
setPartNumber(partNumber);
setPartDescription(partDescription);
setQuantity(quantity);
setPricePerItem(pricePerItem);
}
Using setters inside the constructor ensures that validation rules (like non-negative quantity) are applied immediately.
Step 3: Implement Getter and Setter Methods
Each instance variable needs a getter and a setter. The setters for quantity and pricePerItem must enforce the rule: if the value is not positive, set it to 0 (or 0.0).
// Getter and setter for partNumber
public String getPartNumber() {
return partNumber;
}
public void setPartNumber(String partNumber) {
this.partNumber = partNumber;
}
// Getter and setter for partDescription
public String getPartDescription() {
return partDescription;
}
public void setPartDescription(String partDescription) {
this.partDescription = partDescription;
}
// Getter and setter for quantity
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = (quantity > 0) ? quantity : 0;
}
// Getter and setter for pricePerItem
public double getPricePerItem() {
return pricePerItem;
}
public void setPricePerItem(double pricePerItem) {
this.pricePerItem = (pricePerItem > 0.0) ? pricePerItem : 0.0;
}
Notice the use of ternary operators for concise validation. This approach is clean and efficient.
Step 4: Add getInvoiceAmount Method
This method calculates the total invoice amount by multiplying quantity by price per item. Since validation already ensures non-negative values, we can simply multiply and return the result as a double.
public double getInvoiceAmount() {
return quantity * pricePerItem;
}
That's it! The class is complete. Now we need a test application to demonstrate its capabilities.
Step 5: Create the InvoiceTest Application
Create a new file named InvoiceTest.java. This class will instantiate five Invoice objects with hardcoded data for common hardware store items. We'll also use DecimalFormat to format the subtotal with a dollar sign and commas.
import java.text.DecimalFormat;
/**
* InvoiceTest class to demonstrate the Invoice class.
* @author Your Name
* @version 1.0
*/
public class InvoiceTest {
public static void main(String[] args) {
// Create five Invoice objects
Invoice invoice1 = new Invoice("AB-23-4312", "Cordless Drill", 10, 189.00);
Invoice invoice2 = new Invoice("CD-45-6789", "Hammer", 25, 14.99);
Invoice invoice3 = new Invoice("EF-01-2345", "Phillips Head Screwdriver", 50, 5.49);
Invoice invoice4 = new Invoice("GH-67-8901", "Light Switch", 100, 3.25);
Invoice invoice5 = new Invoice("IJ-12-3456", "Carpenter's Square", 30, 12.75);
// Create DecimalFormat object for currency formatting
DecimalFormat currencyFormat = new DecimalFormat("$#,##0.00");
// Print each invoice
printInvoice(invoice1, 1, currencyFormat);
printInvoice(invoice2, 2, currencyFormat);
printInvoice(invoice3, 3, currencyFormat);
printInvoice(invoice4, 4, currencyFormat);
printInvoice(invoice5, 5, currencyFormat);
}
public static void printInvoice(Invoice invoice, int number, DecimalFormat currencyFormat) {
System.out.println("Invoice #" + number);
System.out.println("Part No.: " + invoice.getPartNumber());
System.out.println("Item Desc.: " + invoice.getPartDescription());
System.out.println("Quantity: " + invoice.getQuantity());
System.out.println("Item Price: " + currencyFormat.format(invoice.getPricePerItem()));
System.out.println("Invoice Subtotal: " + currencyFormat.format(invoice.getInvoiceAmount()));
System.out.println("*********************************");
}
}
The printInvoice method uses the DecimalFormat object to format both the item price and the subtotal. The pattern $#,##0.00 ensures a dollar sign, commas for thousands, and two decimal places. This pattern works for values up to $99,999,999.99 as required.
Testing and Expected Output
When you run InvoiceTest, you should see output similar to this:
Invoice #1
Part No.: AB-23-4312
Item Desc.: Cordless Drill
Quantity: 10
Item Price: $189.00
Invoice Subtotal: $1,890.00
*********************************
Invoice #2
Part No.: CD-45-6789
Item Desc.: Hammer
Quantity: 25
Item Price: $14.99
Invoice Subtotal: $374.75
*********************************
Invoice #3
Part No.: EF-01-2345
Item Desc.: Phillips Head Screwdriver
Quantity: 50
Item Price: $5.49
Invoice Subtotal: $274.50
*********************************
Invoice #4
Part No.: GH-67-8901
Item Desc.: Light Switch
Quantity: 100
Item Price: $3.25
Invoice Subtotal: $325.00
*********************************
Invoice #5
Part No.: IJ-12-3456
Item Desc.: Carpenter's Square
Quantity: 30
Item Price: $12.75
Invoice Subtotal: $382.50
*********************************
Notice that all amounts are properly formatted with a dollar sign and commas. The subtotals are correct multiplications.
Real-World Connection: E-commerce and Inventory Systems
This simple Invoice class mirrors the core of any e-commerce system—think of Amazon or Shopify. Each product in their database is like an Invoice object with a part number (SKU), description, quantity in stock, and price. The getInvoiceAmount method is akin to calculating the total for a line item in a shopping cart. Understanding this pattern helps you build more complex systems, such as inventory management or point-of-sale software.
Moreover, the validation logic (setting negative values to zero) is a common business rule. For example, if a customer returns more items than they purchased, the quantity shouldn't go negative. This kind of defensive programming prevents errors downstream.
Common Pitfalls and Tips
- Forgetting to use setters in the constructor: Always call setters to ensure validation runs even during object creation.
- Incorrect DecimalFormat pattern: Make sure the pattern matches the maximum required value. Test with edge cases like $99,999,999.99.
- Printing unformatted doubles: Always format currency values to avoid ugly representations like 189.0 or 1.89E2.
- Typo in method names: Double-check that getter/setter names match exactly (e.g.,
getPartNumbernotgetpartNumber).
Conclusion
You've now built a complete Java class with encapsulation, constructors, getters/setters, and a utility method. The InvoiceTest application demonstrates creating objects and formatting output. This foundation is essential for more advanced topics like inheritance, polymorphism, and collections.
Remember to add the required documentation box at the top of both files and submit them before the deadline. Good luck with your CSCI 470/502 assignment!