diff --git a/src/Lecture4_interfaces_abstract_classes/BankAccount.java b/src/Lecture4_interfaces_abstract_classes/BankAccount.java
index 28d0d07..6009c01 100644
--- a/src/Lecture4_interfaces_abstract_classes/BankAccount.java
+++ b/src/Lecture4_interfaces_abstract_classes/BankAccount.java
@@ -1,7 +1,13 @@
package Lecture4_interfaces_abstract_classes;
+/**
+ * Represents a simple bank account with a balance.
+ * Starter code preserved; credit() and debit() helpers added
+ * so transaction classes don't need to call setBalance() directly.
+ */
public class BankAccount {
private double balance;
+
public BankAccount(double balance) {
this.balance = balance;
}
@@ -13,4 +19,19 @@ public double getBalance() {
public void setBalance(double balance) {
this.balance = balance;
}
-}
+
+ /** Adds amount to the balance (used by DepositTransaction). */
+ public void credit(double amount) {
+ this.balance += amount;
+ }
+
+ /** Subtracts amount from the balance (used by WithdrawalTransaction). */
+ public void debit(double amount) {
+ this.balance -= amount;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("BankAccount[Balance=%.2f]", balance);
+ }
+}
\ No newline at end of file
diff --git a/src/Lecture4_interfaces_abstract_classes/BaseTransaction.java b/src/Lecture4_interfaces_abstract_classes/BaseTransaction.java
index ed81eb8..3c134bb 100644
--- a/src/Lecture4_interfaces_abstract_classes/BaseTransaction.java
+++ b/src/Lecture4_interfaces_abstract_classes/BaseTransaction.java
@@ -1,51 +1,84 @@
package Lecture4_interfaces_abstract_classes;
import org.jetbrains.annotations.NotNull;
-
import java.util.Calendar;
-public abstract class BaseTransaction implements TransactionInterface {
+/**
+ * Q1 - Concrete class that implements TransactionInterface.
+ *
+ * The starter code made this abstract; the assignment asks us to make it
+ * a CONCRETE class that fully implements the interface, while ensuring
+ * its apply() behaves differently from the subclasses.
+ *
+ * Fields match the starter code exactly (int amount, Calendar date, String transactionID).
+ */
+public class BaseTransaction implements TransactionInterface {
+
+ // --- Fields (preserved from starter code) ---
private final int amount;
private final Calendar date;
private final String transactionID;
/**
- * Lecture1_adt.TransactionInterface Constructor
- * @param amount in an integer
- * @param date: Not null, and must be a Calendar object
- * @return void
- * Instialises the field, attributes of a transaction
- * Creates a object of this
+ * Constructor - mirrors the starter code signature.
+ *
+ * @param amount integer transaction amount
+ * @param date must not be null; defensively copied to preserve invariants
*/
- public BaseTransaction(int amount, @NotNull Calendar date) {
- this.amount = amount;
- this.date = (Calendar) date.clone();
- int uniq = (int) Math.random()*10000;
- transactionID = date.toString()+uniq;
+ public BaseTransaction(int amount, @NotNull Calendar date) {
+ this.amount = amount;
+ this.date = (Calendar) date.clone(); // defensive copy (from lecture)
+ int uniq = (int)(Math.random() * 10000);
+ this.transactionID = date.toString() + uniq;
}
- /**
- * getAmount()
- * @return integer
- */
+ // ---------------------------------------------------------------
+ // Q1 – Getter implementations (from TransactionInterface)
+ // ---------------------------------------------------------------
+
+ @Override
public double getAmount() {
- return amount; // Because we are dealing with Value types we need not worry about what we return
+ return amount; // int → double widening; value type, safe to return directly
}
- /**
- * getDate()
- * @return Calendar Object
- */
+ @Override
public Calendar getDate() {
-// return date; // Because we are dealing with Reference types we need to judiciously copy what our getters return
- return (Calendar) date.clone(); // Defensive copying or Judicious Copying
+ return (Calendar) date.clone(); // defensive / judicious copy (from lecture)
}
- // Method to get a unique identifier for the transaction
- public String getTransactionID(){
- return transactionID;
+ @Override
+ public String getTransactionID() {
+ return transactionID;
+ }
+
+ // ---------------------------------------------------------------
+ // Q1 – printTransactionDetails()
+ // ---------------------------------------------------------------
+
+ @Override
+ public void printTransactionDetails() {
+ System.out.println("=== Base Transaction Details ===");
+ System.out.println(" Transaction ID : " + transactionID);
+ System.out.printf (" Amount : %.2f%n", (double) amount);
+ System.out.printf (" Date : %tF%n", date);
+ System.out.println(" Type : BaseTransaction");
+ }
+
+ // ---------------------------------------------------------------
+ // Q1 – apply()
+ //
+ // BaseTransaction.apply() is intentionally DIFFERENT from the
+ // subclass overrides: it is a neutral / informational application
+ // that logs the call but does NOT modify the account balance.
+ // This makes the base behaviour clearly distinct from Deposit
+ // (which credits) and Withdrawal (which debits).
+ // ---------------------------------------------------------------
+
+ @Override
+ public void apply(BankAccount ba) {
+ System.out.printf(
+ "[BaseTransaction] apply() called — Amount: %.2f | Balance unchanged at: %.2f%n",
+ (double) amount, ba.getBalance()
+ );
}
- // Method to print a transaction receipt or details
- public abstract void printTransactionDetails();
- public abstract void apply(BankAccount ba);
-}
+}
\ No newline at end of file
diff --git a/src/Lecture4_interfaces_abstract_classes/DepositTrasaction.java b/src/Lecture4_interfaces_abstract_classes/DepositTrasaction.java
index 81afab5..8483338 100644
--- a/src/Lecture4_interfaces_abstract_classes/DepositTrasaction.java
+++ b/src/Lecture4_interfaces_abstract_classes/DepositTrasaction.java
@@ -1,30 +1,54 @@
package Lecture4_interfaces_abstract_classes;
import org.jetbrains.annotations.NotNull;
-
import java.util.Calendar;
+/**
+ * Q1 & Q2 - Concrete class for deposit transactions.
+ *
+ * Deposits are IRREVERSIBLE by design (assignment spec, Q2).
+ * Overrides apply() to credit the BankAccount.
+ *
+ * Note: class name intentionally matches starter code spelling ("Trasaction").
+ */
public class DepositTrasaction extends BaseTransaction {
- public DepositTrasaction(int amount, @NotNull Calendar date){
+
+ // --- Constructor ---
+ public DepositTrasaction(int amount, @NotNull Calendar date) {
super(amount, date);
}
- private boolean checkDepositAmount(int amt){
- if (amt < 0){
- return false;
- }
- else{
- return true;
- }
+
+ // Helper preserved from starter code
+ private boolean checkDepositAmount(int amt) {
+ return amt >= 0;
}
- // Method to print a transaction receipt or details
- public void printTransactionDetails(){
- System.out.println("Deposit Trasaction: "+this.toString());
+ // ---------------------------------------------------------------
+ // Q1 – Override apply()
+ // Credits the account; clearly different from BaseTransaction.apply()
+ // which makes no balance change, and from WithdrawalTransaction
+ // which debits.
+ // ---------------------------------------------------------------
+
+ @Override
+ public void apply(BankAccount ba) {
+ ba.credit(getAmount());
+ System.out.printf(
+ "[DepositTransaction] Credited %.2f → New balance: %.2f%n",
+ getAmount(), ba.getBalance()
+ );
}
- public void apply(BankAccount ba){
- double curr_balance = ba.getBalance();
- double new_balance = curr_balance + getAmount();
- ba.setBalance(new_balance);
+ // ---------------------------------------------------------------
+ // Q1 – Override printTransactionDetails()
+ // ---------------------------------------------------------------
+
+ @Override
+ public void printTransactionDetails() {
+ System.out.println("=== Deposit Transaction Details ===");
+ System.out.println(" Transaction ID : " + getTransactionID());
+ System.out.printf (" Amount : +%.2f%n", getAmount());
+ System.out.printf (" Date : %tF%n", getDate());
+ System.out.println(" Type : Deposit (irreversible)");
}
-}
+}
\ No newline at end of file
diff --git a/src/Lecture4_interfaces_abstract_classes/InsufficientFundsException.java b/src/Lecture4_interfaces_abstract_classes/InsufficientFundsException.java
new file mode 100644
index 0000000..61a5e8e
--- /dev/null
+++ b/src/Lecture4_interfaces_abstract_classes/InsufficientFundsException.java
@@ -0,0 +1,29 @@
+package Lecture4_interfaces_abstract_classes;
+
+/**
+ * Q3 - Custom checked exception for insufficient funds.
+ *
+ * Extends Exception (not RuntimeException) so it is a proper checked exception
+ * and callers are forced to handle or declare it — demonstrating Java's
+ * exception hierarchy through inheritance.
+ */
+public class InsufficientFundsException extends Exception {
+
+ private final double amountRequested;
+ private final double amountAvailable;
+
+ public InsufficientFundsException(double amountRequested, double amountAvailable) {
+ super(String.format(
+ "Insufficient funds: requested %.2f but only %.2f is available.",
+ amountRequested, amountAvailable
+ ));
+ this.amountRequested = amountRequested;
+ this.amountAvailable = amountAvailable;
+ }
+
+ public double getAmountRequested() { return amountRequested; }
+ public double getAmountAvailable() { return amountAvailable; }
+
+ /** Convenience: how much short the account is. */
+ public double getShortfall() { return amountRequested - amountAvailable; }
+}
\ No newline at end of file
diff --git a/src/Lecture4_interfaces_abstract_classes/TransactionInterface.java b/src/Lecture4_interfaces_abstract_classes/TransactionInterface.java
index 5902713..b758909 100644
--- a/src/Lecture4_interfaces_abstract_classes/TransactionInterface.java
+++ b/src/Lecture4_interfaces_abstract_classes/TransactionInterface.java
@@ -1,9 +1,12 @@
package Lecture4_interfaces_abstract_classes;
+
import java.util.Calendar;
/**
- * Interface for Transactions
- * Any class that defines a transaction is expected to implement this Interface
+ * Interface for Transactions.
+ * Any class that defines a transaction is expected to implement this interface.
+ *
+ * Q1: Extended with printTransactionDetails() and apply() as required by the assignment.
*/
public interface TransactionInterface {
@@ -16,6 +19,9 @@ public interface TransactionInterface {
// Method to get a unique identifier for the transaction
String getTransactionID();
-}
-
+ // Method to print transaction details (Q1)
+ void printTransactionDetails();
+ // Method to apply the transaction to a BankAccount (Q1)
+ void apply(BankAccount ba);
+}
\ No newline at end of file
diff --git a/src/Lecture4_interfaces_abstract_classes/WithdrawalTransaction.java b/src/Lecture4_interfaces_abstract_classes/WithdrawalTransaction.java
index face5b6..df03959 100644
--- a/src/Lecture4_interfaces_abstract_classes/WithdrawalTransaction.java
+++ b/src/Lecture4_interfaces_abstract_classes/WithdrawalTransaction.java
@@ -1,45 +1,163 @@
package Lecture4_interfaces_abstract_classes;
import org.jetbrains.annotations.NotNull;
-
import java.util.Calendar;
+/**
+ * Q1, Q2 & Q3 - Concrete class for withdrawal transactions.
+ *
+ * Withdrawals are REVERSIBLE by design (assignment spec, Q2).
+ *
+ * Q1 – Overrides apply() to debit the BankAccount.
+ * Q2 – Implements reverse() to restore the account to its pre-withdrawal balance.
+ * Q3 – Overloaded apply(BankAccount, boolean) uses the 'throws' keyword and a
+ * try { } catch { } finally { } block; handles the partial-withdrawal case.
+ */
public class WithdrawalTransaction extends BaseTransaction {
+
+ // Tracks the account this transaction was applied to (needed for reverse())
+ private BankAccount appliedAccount = null;
+
+ // Records any amount that could NOT be withdrawn in a partial withdrawal
+ private double amountNotWithdrawn = 0.0;
+
+ // Guards against reversing a transaction that was never successfully applied
+ private boolean isApplied = false;
+
+ // --- Constructor (matches starter code signature) ---
public WithdrawalTransaction(int amount, @NotNull Calendar date) {
super(amount, date);
}
+ // Helper preserved from starter code
private boolean checkDepositAmount(int amt) {
- if (amt < 0) {
- return false;
- } else {
- return true;
+ return amt >= 0;
+ }
+
+ // ---------------------------------------------------------------
+ // Q1 – Override apply()
+ //
+ // Simple debit; delegates to the overloaded version.
+ // Any InsufficientFundsException is caught here and reported so
+ // that this override still satisfies the void / no-throws signature
+ // required by the interface.
+ // ---------------------------------------------------------------
+
+ @Override
+ public void apply(BankAccount ba) {
+ try {
+ apply(ba, false);
+ } catch (InsufficientFundsException e) {
+ System.err.println("[WithdrawalTransaction] apply() blocked: " + e.getMessage());
}
}
- // Method to reverse the transaction
- public boolean reverse() {
- return true;
- } // return true if reversal was successful
+ // ---------------------------------------------------------------
+ // Q3 – Overloaded apply() with full exception handling
+ //
+ // @param ba the account to debit
+ // @param allowPartial if true: when 0 < balance < amount, withdraw
+ // all available balance and record the shortfall.
+ // if false: throw InsufficientFundsException.
+ //
+ // Uses: throws keyword (method signature)
+ // try / catch / finally block (inside the method body)
+ // ---------------------------------------------------------------
- // Method to print a transaction receipt or details
- public void printTransactionDetails() {
- System.out.println("Deposit Trasaction: " + this.toString());
+ public void apply(BankAccount ba, boolean allowPartial) throws InsufficientFundsException {
+
+ try {
+ double balance = ba.getBalance();
+
+ // Case A: account is empty or overdrawn
+ if (balance <= 0) {
+ throw new InsufficientFundsException(getAmount(), balance);
+ }
+
+ // Case B: partial withdrawal allowed and 0 < balance < amount
+ if (allowPartial && balance < getAmount()) {
+ amountNotWithdrawn = getAmount() - balance;
+ ba.debit(balance); // withdraw everything available
+ appliedAccount = ba;
+ isApplied = true;
+
+ System.out.printf(
+ "[WithdrawalTransaction] Partial withdrawal: debited %.2f. "
+ + "Amount not withdrawn: %.2f. New balance: %.2f%n",
+ balance, amountNotWithdrawn, ba.getBalance()
+ );
+
+ } else if (balance < getAmount()) {
+ // Case C: insufficient funds, partial NOT allowed → throw
+ throw new InsufficientFundsException(getAmount(), balance);
+
+ } else {
+ // Case D: sufficient funds → normal full debit
+ ba.debit(getAmount());
+ appliedAccount = ba;
+ isApplied = true;
+
+ System.out.printf(
+ "[WithdrawalTransaction] Debited %.2f. New balance: %.2f%n",
+ getAmount(), ba.getBalance()
+ );
+ }
+
+ } catch (InsufficientFundsException e) {
+ // Log and re-throw so the caller can decide how to handle it
+ System.err.println("[WithdrawalTransaction] Caught: " + e.getMessage());
+ throw e;
+
+ } finally {
+ // Always runs — suitable for audit/logging regardless of outcome
+ System.out.println("[WithdrawalTransaction] apply() finished for account balance: "
+ + ba.getBalance());
+ }
}
- /*
- Oportunity for assignment: implementing different form of withdrawal
- */
- public void apply(BankAccount ba) {
- double curr_balance = ba.getBalance();
- if (curr_balance > getAmount()) {
- double new_balance = curr_balance - getAmount();
- ba.setBalance(new_balance);
+ // ---------------------------------------------------------------
+ // Q2 – reverse()
+ //
+ // Reverses a previously applied withdrawal by crediting the exact
+ // amount that was debited back to the original BankAccount.
+ // Returns true on success, false if the transaction was never applied.
+ // ---------------------------------------------------------------
+
+ public boolean reverse() {
+ if (!isApplied || appliedAccount == null) {
+ System.out.println("[WithdrawalTransaction] reverse() failed: transaction not yet applied.");
+ return false;
}
+
+ // Amount actually debited = requested amount minus whatever was not withdrawn
+ double actuallyDebited = getAmount() - amountNotWithdrawn;
+ appliedAccount.credit(actuallyDebited);
+
+ System.out.printf(
+ "[WithdrawalTransaction] Reversed: credited %.2f back. Restored balance: %.2f%n",
+ actuallyDebited, appliedAccount.getBalance()
+ );
+
+ // Reset state so the transaction cannot be reversed twice
+ isApplied = false;
+ appliedAccount = null;
+
+ return true;
}
- /*
- Assignment 1 Q3: Write the Reverse method - a method unique to the WithdrawalTransaction Class
- */
-}
+ // ---------------------------------------------------------------
+ // Q1 – Override printTransactionDetails()
+ // ---------------------------------------------------------------
+
+ @Override
+ public void printTransactionDetails() {
+ System.out.println("=== Withdrawal Transaction Details ===");
+ System.out.println(" Transaction ID : " + getTransactionID());
+ System.out.printf (" Amount : -%.2f%n", getAmount());
+ System.out.printf (" Date : %tF%n", getDate());
+ System.out.printf (" Amount Not Withdrawn : %.2f%n", amountNotWithdrawn);
+ System.out.println(" Type : Withdrawal (reversible)");
+ }
+ public double getAmountNotWithdrawn() { return amountNotWithdrawn; }
+}
\ No newline at end of file
diff --git a/src/Main.java b/src/Main.java
index 584a048..7ee0ddc 100644
--- a/src/Main.java
+++ b/src/Main.java
@@ -1,77 +1,56 @@
-import Lecture1_adt.*; // Import all classes from Lecture1_adt package to be used in this client code
+import Lecture1_adt.*;
+import Lecture4_interfaces_abstract_classes.*;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.ArrayList;
import java.util.List;
-//TIP To Run code, press or
-// click the icon in the gutter.
-/*
-* Client Code for accessing the Lecture1_adt.TransactionInterface.java module
+/**
+ * Client Code - Main class.
+ *
+ * Existing lecture test functions are preserved unchanged.
+ * Q4 adds testDepositTransaction(), testWithdrawalTransaction(),
+ * and testPolymorphism() below.
*/
public class Main {
+ // ================================================================
+ // Existing lecture test functions (unchanged)
+ // ================================================================
+
public static void testTransaction1() {
- Calendar d1 = new GregorianCalendar(); // d1 is an Object [Objects are Reference types]
- Lecture1_adt.Transaction1 t1 = new Lecture1_adt.Transaction1(1000, d1); // amount and d1 are arguments
+ Calendar d1 = new GregorianCalendar();
+ Lecture1_adt.Transaction1 t1 = new Lecture1_adt.Transaction1(1000, d1);
System.out.println(t1.toString());
System.out.println("Lecture1_adt.TransactionInterface Amount: \t " + t1.amount);
- System.out.println("Lecture1_adt.TransactionInterface Date: \t " + t1.date);
-
- // Please note that the Client Codes can access the data in the class directly through the dot operator
- // This kind of exposure is a threat to both the Representation Independence and Preservation of Invariants
+ System.out.println("Lecture1_adt.TransactionInterface Date: \t " + t1.date);
}
-
- /** @return a transaction of same amount as t, one month later
- * This is a PRODUCER of the class Lecture1_adt.Transaction2
- * This code will help demostrate the Design exposures still present in transaction2 class
- * */
-
public static Transaction2 makeNextPayment(Transaction2 t) {
- Calendar d = t.getDate();
+ Calendar d = t.getDate();
d.add(Calendar.MONTH, 1);
return new Transaction2(t.getAmount(), d);
}
- /*
- Testing Transaction2 class
- */
public static void testTransaction2() {
-
Calendar d1 = new GregorianCalendar();
-
- Lecture1_adt.Transaction2 t = new Lecture1_adt.Transaction2(1000, d1);
-
+ Lecture1_adt.Transaction2 t = new Lecture1_adt.Transaction2(1000, d1);
Lecture1_adt.Transaction2 modified_t = makeNextPayment(t);
System.out.println("\n\nState of the Object T1 After Client Code Tried to Change the Amount");
- System.out.println("Lecture1_adt.TransactionInterface Amount: \t "+modified_t.getAmount());
- System.out.println("Lecture1_adt.TransactionInterface Date: \t "+modified_t.getDate().getTime());
+ System.out.println("Lecture1_adt.TransactionInterface Amount: \t " + modified_t.getAmount());
+ System.out.println("Lecture1_adt.TransactionInterface Date: \t " + modified_t.getDate().getTime());
System.out.println("\n\nHow does T2 Look Like?????");
- System.out.println("Lecture1_adt.TransactionInterface Amount: \t "+modified_t.getAmount());
- System.out.println("Lecture1_adt.TransactionInterface Date: \t "+modified_t.getDate().getTime());
-
- /* Please note that Although we have solved the problem of Transaction1
- * And client code can no longer use the dot (.) operator to directly access the data
- * There is still some exposure especially if we pass an object of a previous Transaction2 to create a new Transaction2 object
- */
-
+ System.out.println("Lecture1_adt.TransactionInterface Amount: \t " + modified_t.getAmount());
+ System.out.println("Lecture1_adt.TransactionInterface Date: \t " + modified_t.getDate().getTime());
}
-
- /** @return a list of 12 monthly payments of identical amounts
- * This code will help demostrate the Design exposures still present in transaction3 class
- * */
- public static List makeYearOfPayments (int amount) throws NullPointerException {
-
- List listOfTransaction3s = new ArrayList();
+ public static List makeYearOfPayments(int amount) throws NullPointerException {
+ List listOfTransaction3s = new ArrayList<>();
Calendar date = new GregorianCalendar(2024, Calendar.JANUARY, 3);
-
-
for (int i = 0; i < 12; i++) {
listOfTransaction3s.add(new Transaction3(amount, date));
date.add(Calendar.MONTH, 1);
@@ -79,42 +58,20 @@ public static List makeYearOfPayments (int amount) throws NullPoin
return listOfTransaction3s;
}
- /*
- Testing Transaction3 class
- */
public static void testTransaction3() {
-
List allPaymentsIn2024 = makeYearOfPayments(1000);
-
for (Transaction3 t3 : allPaymentsIn2024) {
-
- // Display all the 12 Transactions
for (Transaction3 transact : allPaymentsIn2024) {
System.out.println("\n\n ::::::::::::::::::::::::::::::::::::::::::::\n");
- System.out.println("Lecture1_adt.TransactionInterface Amount: \t "+transact.getAmount());
- System.out.println("Lecture1_adt.TransactionInterface Date: \t "+transact.getDate().getTime());
+ System.out.println("Lecture1_adt.TransactionInterface Amount: \t " + transact.getAmount());
+ System.out.println("Lecture1_adt.TransactionInterface Date: \t " + transact.getDate().getTime());
}
}
-
- /* Please Check all the 12 transactions displayed and hwo their dates look like
- * Note that Although Transaction3 class resolves to an extent the exposure in Transaction2 class
- * There is still some exposure especially if we pass an object of a previous Transaction3 to create a
- * new Transaction3 object
- */
}
-
- /** @return a list of 12 monthly payments of identical amounts
- * This code Show that by judicious copying and defensive programming we eliminate the exposure in Transaction3
- * As defined in the constructor of Transaction4 class
- * */
-
- public static List makeYearOfPaymentsFinal (int amount) throws NullPointerException {
-
- List listOfTransaction4s = new ArrayList();
+ public static List makeYearOfPaymentsFinal(int amount) throws NullPointerException {
+ List listOfTransaction4s = new ArrayList<>();
Calendar date = new GregorianCalendar(2024, Calendar.JANUARY, 3);
-
-
for (int i = 0; i < 12; i++) {
listOfTransaction4s.add(new Transaction4(amount, date));
date.add(Calendar.MONTH, 1);
@@ -122,35 +79,159 @@ public static List makeYearOfPaymentsFinal (int amount) throws Nul
return listOfTransaction4s;
}
- /*
- Testing Transaction3 class
- */
public static void testTransaction4() {
-
- /*
- * Call the function to make all the Twelve transaction in a year of our business
- */
-
List transactionsIn2024 = makeYearOfPaymentsFinal(1200);
-
- // Display all the 12 Transactions
for (Transaction4 transact : transactionsIn2024) {
System.out.println("\n\n ::::::::::::::::::::::::::::::::::::::::::::\n");
- System.out.println("Lecture1_adt.TransactionInterface Amount: \t "+transact.getAmount());
- System.out.println("Lecture1_adt.TransactionInterface Date: \t "+transact.getDate().getTime());
+ System.out.println("Lecture1_adt.TransactionInterface Amount: \t " + transact.getAmount());
+ System.out.println("Lecture1_adt.TransactionInterface Date: \t " + transact.getDate().getTime());
}
+ }
- // Please Take a look at all the 12 transaction now and compare with the outputs of the Transaction3 class
+
+ // ================================================================
+ // Q4 – New client code: DepositTransaction tests
+ // ================================================================
+
+ public static void testDepositTransaction() {
+ System.out.println("\n============================================================");
+ System.out.println(" TEST: DepositTransaction");
+ System.out.println("============================================================");
+
+ Calendar date = new GregorianCalendar();
+ BankAccount account = new BankAccount(500.00);
+ System.out.println("Initial balance: " + account.getBalance());
+
+ // --- Subtype object, subtype reference ---
+ DepositTrasaction deposit = new DepositTrasaction(200, date);
+ deposit.printTransactionDetails();
+ deposit.apply(account);
+ System.out.println("Balance after deposit: " + account.getBalance());
+
+ // --- Type-cast subtype to supertype, then call apply() ---
+ // The reference is BaseTransaction (supertype), object is DepositTrasaction.
+ // Java resolves apply() at RUNTIME (late binding / dynamic dispatch),
+ // so DepositTrasaction.apply() is called — not BaseTransaction.apply().
+ System.out.println("\n-- Type-casting DepositTrasaction → BaseTransaction --");
+ BaseTransaction castedDeposit = (BaseTransaction) new DepositTrasaction(100, date);
+ castedDeposit.printTransactionDetails();
+ castedDeposit.apply(account); // late binding → DepositTrasaction.apply()
+ System.out.println("Balance after casted deposit: " + account.getBalance());
}
- public static void main(String[] args) {
- // This is the client code
- // Uncomment the following lines to test the class which you would like to test
+ // ================================================================
+ // Q4 – New client code: WithdrawalTransaction tests
+ // ================================================================
+
+ public static void testWithdrawalTransaction() {
+ System.out.println("\n============================================================");
+ System.out.println(" TEST: WithdrawalTransaction");
+ System.out.println("============================================================");
+
+ Calendar date = new GregorianCalendar();
+ BankAccount account = new BankAccount(1000.00);
+ System.out.println("Initial balance: " + account.getBalance());
+
+ // --- Normal withdrawal (sufficient funds) ---
+ WithdrawalTransaction w1 = new WithdrawalTransaction(300, date);
+ w1.printTransactionDetails();
+ w1.apply(account);
+ System.out.println("Balance after withdrawal: " + account.getBalance());
+
+ // --- Reverse the withdrawal (Q2) ---
+ System.out.println("\n-- Reversing withdrawal --");
+ boolean reversed = w1.reverse();
+ System.out.println("Reversal successful: " + reversed);
+ System.out.println("Balance after reversal: " + account.getBalance());
+
+ // --- Insufficient funds, allowPartial = false (Q3, throws) ---
+ System.out.println("\n-- Withdrawal exceeding balance (allowPartial=false) --");
+ WithdrawalTransaction w2 = new WithdrawalTransaction(9999, date);
+ try {
+ w2.apply(account, false);
+ } catch (InsufficientFundsException e) {
+ System.out.println("Caught InsufficientFundsException: " + e.getMessage());
+ System.out.printf ("Shortfall: %.2f%n", e.getShortfall());
+ }
+ System.out.println("Balance unchanged: " + account.getBalance());
+
+ // --- Partial withdrawal: 0 < balance < amount (Q3) ---
+ System.out.println("\n-- Partial withdrawal (allowPartial=true, balance < amount) --");
+ BankAccount smallAccount = new BankAccount(80.00);
+ WithdrawalTransaction w3 = new WithdrawalTransaction(200, date);
+ try {
+ w3.apply(smallAccount, true);
+ } catch (InsufficientFundsException e) {
+ System.err.println("Unexpected exception: " + e.getMessage());
+ }
+ w3.printTransactionDetails();
+ System.out.println("Balance after partial withdrawal: " + smallAccount.getBalance());
+ }
+
+
+ // ================================================================
+ // Q4 – New client code: Polymorphism (early vs. late binding)
+ // ================================================================
+
+ public static void testPolymorphism() {
+ System.out.println("\n============================================================");
+ System.out.println(" TEST: Polymorphism — Early vs. Late Binding");
+ System.out.println("============================================================");
+
+ Calendar date = new GregorianCalendar();
+ BankAccount account = new BankAccount(500.00);
- // testTransaction1()
- // testTransaction2()
- // testTransaction3()
- // testTransaction4()
+ /*
+ * EARLY BINDING (compile-time / static dispatch):
+ * The reference type AND the object type are both BaseTransaction.
+ * The compiler resolves apply() to BaseTransaction.apply() at
+ * compile time because there is no subtype to dispatch to.
+ * Result: no balance change (BaseTransaction.apply() is informational).
+ */
+ System.out.println("-- Early Binding: BaseTransaction reference, BaseTransaction object --");
+ BaseTransaction earlyBound = new BaseTransaction(50, date);
+ earlyBound.apply(account);
+ System.out.println("Balance (unchanged): " + account.getBalance());
+
+ /*
+ * LATE BINDING (runtime / dynamic dispatch):
+ * The reference type is BaseTransaction (supertype), but the actual
+ * object at runtime is DepositTrasaction (subtype).
+ * The JVM looks up the method in the actual object's class at runtime
+ * and dispatches to DepositTrasaction.apply() → balance increases.
+ */
+ System.out.println("\n-- Late Binding: BaseTransaction reference, DepositTrasaction object --");
+ BaseTransaction lateDeposit = new DepositTrasaction(150, date);
+ lateDeposit.apply(account); // JVM dispatches to DepositTrasaction.apply()
+ System.out.println("Balance after late-bound deposit: " + account.getBalance());
+
+ /*
+ * LATE BINDING with WithdrawalTransaction:
+ * Same principle — the supertype reference holds a WithdrawalTransaction.
+ * apply() resolves to WithdrawalTransaction.apply() at runtime.
+ */
+ System.out.println("\n-- Late Binding: BaseTransaction reference, WithdrawalTransaction object --");
+ BaseTransaction lateWithdraw = new WithdrawalTransaction(100, date);
+ lateWithdraw.apply(account); // JVM dispatches to WithdrawalTransaction.apply()
+ System.out.println("Balance after late-bound withdrawal: " + account.getBalance());
+ }
+
+
+ // ================================================================
+ // main()
+ // ================================================================
+
+ public static void main(String[] args) {
+ // Existing lecture tests (uncomment as needed)
+ // testTransaction1();
+ // testTransaction2();
+ // testTransaction3();
+ // testTransaction4();
+
+ // Q4 – Assignment tests
+ testDepositTransaction();
+ testWithdrawalTransaction();
+ testPolymorphism();
}
}
\ No newline at end of file