Inheritance Abstract classes Inheritance-based polymorphism, encapsulation
Inheritance
- Usage (Format):
A class (subclass) inherits attributes and methods from another class (superclass) using theextends
keyword.
// Superclass
public class Vehicle {
double speed;
void go() {
System.out.println("This vehicle is moving");
}
void stop() {
System.out.println("This vehicle is stopped");
}
}
// Subclass
public class Car extends Vehicle {
// Car inherits speed, go(), stop()
int wheels = 4; // Additional attribute
int doors = 4; // Additional attribute
}
// Main usage
public class Main {
public static void main(String[] args) {
Car car = new Car();
car.go(); // Accesses inherited method
System.out.println(car.speed); // Accesses inherited attribute
System.out.println(car.doors); // Accesses Car's own attribute
}
}
- Purpose: Enables code reusability. Common methods and fields don't need to be rewritten in multiple classes.
-
Terminology:
-
Subclass
(or child class, derived class): The class that inherits (e.g.,Car
). -
Superclass
(or parent class, base class): The class being inherited from (e.g.,Vehicle
). -
Benefits:
-
Reduces code duplication.
- Subclasses can have their own unique additional attributes and methods.
- Establishes an "is-a" relationship (e.g., a
Car
is aVehicle
).
-
Method Overriding
- Usage (Format):
A subclass provides a specific implementation for a method that is already defined in its superclass.
// Superclass
public class Animal {
void speak() {
System.out.println("The animal is speaking");
}
}
// Subclass with overridden method
public class Dog extends Animal {
@Override // Good practice annotation
void speak() {
System.out.println("The dog goes bark");
}
}
// Main usage
public class Main {
public static void main(String[] args) {
Dog dog = new Dog();
dog.speak(); // Calls Dog's speak() method
Animal animal = new Animal();
animal.speak(); // Calls Animal's speak() method
}
}
- Definition: Declaring a method in a subclass with the same signature (name, parameters) as a method in its parent class.
- Purpose: Allows a subclass to provide its own specific behavior for an inherited method.
-
@Override
Annotation:- Not mandatory, but highly recommended.
- Instructs the compiler to check if the method is actually overriding a superclass method. Helps catch errors (e.g., typos in method name or parameters).
- The method in the subclass must have the same name, return type (or a subtype for covariant return types), and parameter list as the method in the superclass.
- Access modifier of the overriding method cannot be more restrictive than the overridden method.
Super Keyword
-
Usage (Format):
Refers to the immediate superclass of an object. -
Calling Superclass Constructor:
- Important Points:// Superclass public class Person { String name; int age; Person(String name, int age) { this.name = name; this.age = age; } public String toString() { return this.name + "\n" + this.age + "\n"; } } // Subclass public class Hero extends Person { String power; Hero(String name, int age, String power) { super(name, age); // Calls Person's constructor this.power = power; } @Override public String toString() { return super.toString() + this.power; // Calls Person's toString() } }
-
Similar to the
this
keyword, butthis
refers to the current object, whilesuper
refers to its superclass portion. -
super()
for Constructor:- Used to call a constructor of the superclass.
- If used, it must be the very first statement in the subclass's constructor.
- If no explicit
super()
call is made, the compiler implicitly inserts a call to the superclass's no-argument constructor (super();
). If the superclass doesn't have a no-argument constructor, this will result in a compile error unless another superclass constructor is explicitly called. -
super.memberName
: Used to access methods or fields of the superclass, especially useful when a subclass has overridden a method or hidden a field and you still need to access the superclass's version.
Abstraction
- Usage (Format):
Hiding implementation details and showing only essential features. Achieved using abstract classes and abstract methods.
// Abstract class with an abstract method
public abstract class Vehicle {
abstract void go(); // Abstract method: no implementation
void stop() { // Concrete method: has implementation
System.out.println("Vehicle stopped.");
}
}
// Concrete subclass implementing the abstract method
public class Car extends Vehicle {
@Override
void go() {
System.out.println("The driver is driving a car");
}
}
// Main usage
public class Main {
public static void main(String[] args) {
// Vehicle vehicle = new Vehicle(); // ERROR! Cannot instantiate abstract class
Car car = new Car();
car.go(); // Calls Car's implementation
car.stop(); // Calls Vehicle's inherited implementation
}
}
-
abstract class
:- Cannot be instantiated directly using
new
. - Meant to be subclassed.
- Can contain both
abstract
methods (without implementation) and concrete methods (with implementation). - If a class contains one or more abstract methods, it must be declared
abstract
. - Can have constructors (called by subclass constructors via
super()
). - Used to define a common template or partial implementation for a group of related subclasses.
-
abstract method
: -
Declared with the
abstract
keyword and has no body (ends with a semicolon). - Example:
abstract void go();
- Forces subclasses to provide an implementation for this method, unless the subclass is also declared
abstract
. - Purpose: Prevents instantiation of overly general classes (e.g., "Vehicle" is too vague; "Car" or "Bicycle" are specific). Enforces a contract for subclasses.
- Cannot be instantiated directly using
Encapsulation
- Usage (Format):
Restricting direct access to some of an object's components (attributes) and providing access through public methods (getters and setters).
public class Car {
private String make; // private attribute
private String model; // private attribute
private int year; // private attribute
// Constructor
public Car(String make, String model, int year) {
this.setMake(make); // Use setter for potential validation
this.setModel(model);
this.setYear(year);
}
// Getter for make
public String getMake() {
return make;
}
// Setter for make
public void setMake(String make) {
// Can add validation logic here
this.make = make;
}
// Getter for model
public String getModel() {
return model;
}
// Setter for model
public void setModel(String model) {
this.model = model;
}
// Getter for year
public int getYear() {
return year;
}
// Setter for year
public void setYear(int year) {
// Example validation
if (year > 1885 && year <= java.time.Year.now().getValue() + 1) {
this.year = year;
} else {
System.out.println("Invalid year.");
// Or throw an exception
}
}
}
// Main usage
// Car myCar = new Car("Toyota", "Camry", 2020);
// System.out.println(myCar.getMake()); // Access via getter
// myCar.setYear(2021); // Modify via setter
- Definition: Bundling data (attributes) and methods that operate on the data within a class, and restricting direct access to the attributes from outside the class.
-
Access Modifiers:
-
private
: Accessible only within the defining class. (Most common for attributes in encapsulation). -
default
(package-private): Accessible within the same package. -
protected
: Accessible within the same package and by subclasses in other packages. -
public
: Accessible from anywhere. - Getters (Accessors): Public methods used to read the values of private attributes (e.g.,
getMake()
). - Setters (Mutators): Public methods used to modify the values of private attributes (e.g.,
setMake(String make)
). Setters can include validation logic. -
Benefits:
-
Data Hiding: Protects an object's internal state from unintended external modification.
- Control: The class has full control over its data through getters and setters.
- Flexibility & Maintainability: Implementation details can change without affecting classes that use it, as long as the public interface (getters/setters) remains the same.
- Recommendation: Make attributes
private
by default unless there's a specific reason for them to be more accessible.
-
Copy Objects
- Usage (Format):
Creating a distinct copy of an object, rather than just copying its reference.
// Assuming Car class from Encapsulation example with getters/setters
// Car car1 = new Car("Honda", "Civic", 2019);
// Car car2 = new Car("Ford", "Focus", 2018);
// Incorrect way (reference copy):
// car1 = car2; // Now car1 and car2 point to the SAME Ford Focus object.
// Changes to car1 affect car2 and vice-versa.
// Better way (shallow copy using setters and getters):
// Car car1 = new Car("Honda", "Civic", 2019);
// Car carToCopyFrom = new Car("Ford", "Focus", 2018);
// car1.setMake(carToCopyFrom.getMake());
// car1.setModel(carToCopyFrom.getModel());
// car1.setYear(carToCopyFrom.getYear());
// // Now car1 is a distinct object with values from carToCopyFrom.
(Note: The provided text is brief. A more robust way is often a copy constructor or implementing Cloneable
and overriding clone()
, but the prompt asks to stick to the text).
- Important Points:
-
Reference vs. Value Copy:
-
A = B;
(where A and B are object references) copies the memory address (reference). Both A and B will point to the same object. -
Deep vs. Shallow Copy:
-
The method described (using getters and setters for primitive/immutable fields) performs a shallow copy if the object contains references to other mutable objects. A deep copy would involve recursively copying those referenced objects as well. The text implies a shallow copy of attributes.
- Goal: To have two independent objects with the same (or derived) state, so modifications to one do not affect the other.
-
Common approaches (beyond the text's scope but for context):
-
Copy Constructor: A constructor that takes an object of the same class as an argument and initializes the new object's fields from the argument object.
-
Cloneable
interface and clone()
method: Java's built-in mechanism, though it has complexities.
-
Interface
- Usage (Format):
A contract defining methods that a class must implement. A class uses theimplements
keyword.
// Interface definition
public interface Prey {
void flee(); // Abstract method (public abstract by default)
}
public interface Predator {
void hunt(); // Abstract method
}
// Class implementing one interface
public class Rabbit implements Prey {
@Override
public void flee() {
System.out.println("The rabbit is fleeing");
}
}
// Class implementing multiple interfaces
public class Fish implements Prey, Predator {
@Override
public void hunt() {
System.out.println("This fish is hunting smaller fish");
}
@Override
public void flee() {
System.out.println("This fish is fleeing from a larger fish");
}
}
// Main usage
public class Main {
public static void main(String[] args) {
Rabbit rabbit = new Rabbit();
rabbit.flee();
Fish fish = new Fish();
fish.hunt();
fish.flee();
}
}
- Definition: An abstract type that is used to specify a behavior that classes must implement. It's a template or a contract.
-
implements
keyword: Used by a class to adopt an interface. - Multiple Interfaces: A class can implement multiple interfaces, separated by commas (e.g.,
class Fish implements Prey, Predator
). This is a way Java achieves a form of multiple inheritance of type. -
Inheritance vs. Interface:
- A class can
extend
only one superclass (single inheritance of implementation). - A class can
implement
multiple interfaces (multiple inheritance of type/contract). -
Contents:
-
Traditionally (pre-Java 8): Only abstract methods (implicitly
public abstract
) and constants (implicitlypublic static final
). - Java 8+: Can also contain
default
methods (with implementation, can be overridden) andstatic
methods (with implementation, belong to the interface, not instances). - Obligation: A concrete (non-abstract) class implementing an interface must provide an implementation for all abstract methods declared in that interface.
- Purpose: To achieve abstraction, polymorphism, and loose coupling. Defines "what" a class can do, not "how" it does it.
- A class can
Polymorphism
- Usage (Format):
The ability of an object to be treated as an instance of its own class, its superclass, or any interface it implements.
// Superclass
public class Vehicle {
public void go() {
System.out.println("The vehicle is generally moving");
}
}
// Subclasses
public class Car extends Vehicle {
@Override
public void go() {
System.out.println("The car begins moving");
}
}
public class Bicycle extends Vehicle {
@Override
public void go() {
System.out.println("The bicycle begins moving");
}
}
public class Boat extends Vehicle {
@Override
public void go() {
System.out.println("The boat begins moving");
}
}
// Main usage
public class Main {
public static void main(String[] args) {
Car car = new Car();
Bicycle bicycle = new Bicycle();
Boat boat = new Boat();
// Polymorphic array: Vehicle reference holding Car, Bicycle, Boat objects
Vehicle[] racers = {car, bicycle, boat};
for (Vehicle x : racers) {
x.go(); // Calls the specific go() method of the actual object type
// (Car's go(), Bicycle's go(), Boat's go())
}
}
}
- Definition: "Many forms." An object can be identified as more than one type (its own type, its superclass type, any interface type it implements).
- Mechanism: Occurs when a superclass reference variable points to a subclass object (e.g.,
Vehicle v = new Car();
). - Benefit: Allows for flexible and generic code. You can write code that operates on the superclass type, and it will work correctly with any subclass objects.
- Requirement for Method Calls: For
x.go()
to be valid in the example, thego()
method must be defined in theVehicle
class (the type of the referencex
). The actual implementation that runs is determined by the object's true type at runtime. -
Polymorphism with
Object
: Since all classes in Java implicitly inherit fromObject
, anObject
reference can hold an instance of any class.-
Object[] things = {car, bicycle, "hello"};
is valid. However, to callCar
-specific methods, you'd need to cast. - Often used with method overriding to achieve different behaviors for the same method call depending on the actual object type.
-
Dynamic Polymorphism
- Usage (Format):
The specific method implementation to be executed is determined at runtime, based on the actual type of the object being referred to. This is a core aspect of polymorphism in OOP.
// Superclass
public class Animal {
public void speak() {
System.out.println("Animal goes brrrr");
}
}
// Subclasses
public class Dog extends Animal {
@Override
public void speak() {
System.out.println("Dog goes bark");
}
}
public class Cat extends Animal {
@Override
public void speak() {
System.out.println("Cat goes meow");
}
}
// Main usage
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
Animal animal; // Superclass reference
System.out.println("What animal do you want? (1=dog) or (2=cat):");
int choice = scanner.nextInt();
if (choice == 1) {
animal = new Dog(); // animal refers to a Dog object
} else if (choice == 2) {
animal = new Cat(); // animal refers to a Cat object
} else {
animal = new Animal(); // animal refers to an Animal object
System.out.println("That choice was invalid.");
}
// Dynamic dispatch: The speak() method of the actual object (Dog, Cat, or Animal)
// is called at runtime.
animal.speak();
scanner.close();
}
}
- Definition: Polymorphism where the decision of which method version to execute is made at runtime (dynamically), not at compile time.
- Also known as: Runtime Polymorphism, Late Binding.
- How it works: When a method is called through a superclass reference, the JVM checks the actual type of the object that the reference points to at runtime and executes the version of the method belonging to that actual type.
- Prerequisite: Method overriding. The method must be defined in the superclass and overridden in the subclass(es).
-
Example Breakdown:
animal.speak();
- If
animal
points to aDog
object,Dog.speak()
is executed. - If
animal
points to aCat
object,Cat.speak()
is executed. - This decision is made "dynamically" when the program is running.
- Key Idea: "A [superclass reference] is a: [subclass type], and a [superclass type], and an [Object type]". The behavior depends on the actual instance.
- If