With polymorphic types, you might expect to want to check the runtime type: Here is a new version of the printAccounts function: public static void printAccounts2(BankAccount[] acct) { String str = null; for(int i = 0; i < acct.length; i++) { if ( acct[i] instanceof SavingsAccount ) { str = String.format("Savings Account(balance = %.2f, rate = %.3f)", getBalance(), interestRate); } else if (acct[i] instanceof BankAccount) { str = String.format("BankAccount(balance = %.2f)", balance); } System.out.println(str); } } Note: This is not as "reusable" as this version: public static void printAccounts(BankAccount[] acct) { String str = null; for(int i = 0; i < acct.length; i++) { str = acct[i].toString(); // Which toString()? System.out.println(str); } } The selection of which toString() at run time is called dynamic dispatch dynamic => runtime dispatch => select which implementation (of toString) The dynamic dispatch feature of object oriented languages is also called polymorphism. The reason this is the same idea as use for polymorphic types is that the same expression can have different meanings at run time: BankAccount a; Expression Meaning a At run time can reference type BankAccount or SavingsAccount a.toString() At run time can use the implementation of toString() in BankAccount or the one in SavingsAccount depending on the run time type of a.