xSKILLby Infirals
Register

Java for Beginners

0 of 24 notes read 0%

Reading progress is saved to your INFIRALS account. Sign in to keep this note marked as read.

Polymorphism, casting, and access control

Inheritance, interfaces and polymorphism12 min readLesson 6 of 6

6.1 One call, several behaviors

Polymorphism lets one variable type refer to objects of several subclasses, with each object running its own version of a method. The call is written once; the object decides which method body runs.

This works when the method is declared in the parent and overridden in the children. The compiler checks the call against the parent type, and the runtime uses the object's class. The mechanism is dynamic dispatch.

Polymorphism keeps loops and methods open to new subclasses. A method that accepts Payment works with a card payment or a class added next month.

java
public class Main {
    public static void main(String[] args) {
        Payment[] payments = { new CardPayment(), new CashPayment() };
        for (Payment payment : payments) {
            payment.pay(250);
        }
    }
}
class Payment {
    void pay(int amount) { System.out.println("Paying " + amount); }
}
class CardPayment extends Payment {
    @Override
    void pay(int amount) { System.out.println("Card " + amount); }
}
class CashPayment extends Payment {
    @Override
    void pay(int amount) { System.out.println("Cash " + amount); }
}

Output

Card 250
Cash 250

6.1.1 Upcasting and downcasting

Assigning a subclass object to a parent variable is an upcast, and it happens automatically. The variable sees the parent's members. Assigning back to the subclass type is a downcast and needs an explicit cast.

A downcast is checked at runtime. If the object is not an instance of the target type, the JVM throws ClassCastException. Check with instanceof before casting when the object type is uncertain.

Java 16 and later support a pattern form: if (payment instanceof CardPayment card) { card.refund(); }. The variable card is already cast and scoped to the true branch.

java
public class Main {
    public static void main(String[] args) {
        Payment payment = new CardPayment();
        if (payment instanceof CardPayment card) {
            card.refund();
        }
        Payment cash = new CashPayment();
        System.out.println(cash instanceof CardPayment);
    }
}
class Payment { }
class CardPayment extends Payment {
    void refund() { System.out.println("Refunded"); }
}
class CashPayment extends Payment { }

Output

Refunded
false

6.1.2 Access modifiers

private limits a member to its own class. No modifier, often called package-private, limits it to the package. protected also allows subclasses in other packages. public allows access from anywhere.

Fields usually start private, with public methods for the operations that should be allowed. Methods that form the class's API are public; helpers stay private.

protected is useful for a member that subclasses need to build on. It also widens the API to every subclass, so use it when inheritance is part of the design.

java
public class Main {
    public static void main(String[] args) {
        Account account = new Account();
        account.deposit(50);
        System.out.println(account.type);
        System.out.println(account.getBalance());
    }
}
class Account {
    private int balance;
    protected String type = "Savings";
    void deposit(int amount) { balance += amount; }
    int getBalance() { return balance; }
}

Output

Savings
50

6.1.3 Static and instance

A static field belongs to the class. One copy exists, and it is read through the class name. A static method also belongs to the class and runs without an object. An instance member belongs to each object, and it is called through a reference.

A static method cannot use this and cannot read instance fields directly. It can accept an object as a parameter and use its public methods. main is static because the launcher calls it before any object exists.

Every object shares the same static field. A counter of created objects is a common example. An instance field is separate for each object, even when the class has a static field too.

java
public class Main {
    public static void main(String[] args) {
        Counter first = new Counter();
        Counter second = new Counter();
        first.increment();
        second.increment();
        System.out.println(first.value);
        System.out.println(Counter.created);
    }
}
class Counter {
    static int created;
    int value;
    Counter() { created++; }
    void increment() { value++; }
}

Output

1
2

6.1.4 Encapsulation in practice

Encapsulation keeps fields private and exposes operations that preserve the class's rules. A setter can reject an impossible value; a getter can return a copy or a derived value.

Validation in one place is easier to trust than checks repeated at every call site. The class can change its internal representation later while the public methods keep the same meaning.

Not every field needs a getter and setter. Expose the operations the outside world needs, and keep the rest internal. A class with a small public surface is easier to maintain.

java
public class Main {
    public static void main(String[] args) {
        Thermostat thermostat = new Thermostat();
        thermostat.setCelsius(24);
        thermostat.setCelsius(-50);
        System.out.println(thermostat.getCelsius());
    }
}
class Thermostat {
    private int celsius;
    void setCelsius(int value) {
        if (value >= 10 && value <= 40) {
            celsius = value;
        }
    }
    int getCelsius() { return celsius; }
}

Output

24

6.1.5 Practice

  1. Create a Payment parent with pay(int) and two subclasses that override it. Call both through a parent array.
  2. Upcast a card payment to Payment, then use instanceof with a pattern to call refund().
  3. Create a class with a private field, a package-private method, and a protected field. Explain which other classes can use each.
  4. Add a static counter to a class and print it after creating three objects.
  5. Add validation to a setter and confirm that an invalid value leaves the field unchanged.

For step 3, write the answer as a table with one row per modifier and one column for each caller: same class, same package, subclass, anywhere.

Interview questions

3 questions from this note. Open one to see the answer; no account needed.

  • 01What is the difference between an upcast and a downcast?

    An upcast stores a subclass object in a parent variable and is automatic. A downcast goes back to the subclass type, needs a cast, and is checked at runtime.

  • 02What does `protected` allow?

    Access from the same package and from subclasses in other packages. It is wider than package-private and narrower than public.

  • 03Why can't a static method use an instance field directly?

    A static method runs without an object, so there is no instance whose field it could read. Pass an object or make the method non-static.