Topic 8: Inheritance in Java
📖 5 min read · 🎯 intermediate · 🧭 Prerequisites: polymorphism-in-java, android-building-blocks-iv-content-providers
Why this matters
Here's the thing — you've already used inheritance without knowing it. Every time you create a new Activity in Android Studio, it says extends AppCompatActivity. That word extends is not decoration. It means your class is borrowing hundreds of built-in features — a screen lifecycle, a back button, a title bar — without you writing a single line to make them work. That's inheritance in action. In this lesson we'll understand exactly what's happening under the hood, so when Android gives you a class to extend, you know what you're getting and why it works.
What You'll Learn
- How to define a superclass (base class) and a subclass (derived class) in Java
- How to inherit and reuse methods from a
BaseActivityacross multiple Android activities - How to create custom Android UI components by extending existing view classes like
AppCompatButton - How to use inherited methods and override them to add custom behavior
The Analogy
Think of a city's building code. The master blueprint defines everything every building must have: load-bearing walls, plumbing hookups, electrical panels. When architects design a library, a school, or a fire station, they start from that master blueprint and add only what makes their building unique — they do not redraw the foundation from scratch. In Java, the superclass is the master blueprint, and each subclass is a specialized building that inherits the foundation and adds its own rooms. Every library still has walls and plumbing; every MainActivity still has showToast and logMessage.
Chapter 1: The Fundamentals of Inheritance
In Java, inheritance allows a class called the subclass (or derived class) to inherit fields and methods from another class called the superclass (or base class). This enables two key benefits:
- Code reuse — common logic lives in one place, not duplicated across every class
- Hierarchical modeling — relationships like "a
MainActivityIS-ABaseActivity" map naturally to the class tree
The keyword that makes this happen is extends. The subclass declares extends SuperclassName, and from that moment it owns every non-private field and method the superclass defines.
public class SubClass extends SuperClass {
// inherits all public and protected members of SuperClass
}
The super keyword lets a subclass call its parent's constructors or methods — essential when you need the parent's setup logic before adding your own.
Chapter 2: Inheriting Activity Behavior — BaseActivity and MainActivity
Imagine building an Android app with a dozen screens. Every screen needs to show toast messages and write log entries. Without inheritance you copy those methods into every Activity. With inheritance you write them once.
1. Base Class: BaseActivity
BaseActivity extends AppCompatActivity (itself part of Android's own inheritance chain) and defines two reusable utilities.
// BaseActivity.java
public class BaseActivity extends AppCompatActivity {
// Common method to show a toast message
public void showToast(String message) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
}
// Common method to log a message
public void logMessage(String tag, String message) {
Log.d(tag, message);
}
}
2. Subclass: MainActivity
MainActivity extends BaseActivity and immediately has access to both utilities — no redeclaration needed.
// MainActivity.java
public class MainActivity extends BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Using the inherited method to show a toast message
showToast("Welcome to MainActivity");
// Using the inherited method to log a message
logMessage("MainActivity", "Activity created");
}
}
super.onCreate(savedInstanceState) calls AppCompatActivity's own onCreate through the chain — skipping it would break Android's lifecycle. The rest of onCreate then calls showToast and logMessage as if they were declared locally, because through inheritance they effectively are.
Chapter 3: Inheritance in Android UI Components
Android's UI system is one of the largest real-world inheritance hierarchies in production software. View is the superclass of every visible element. From it descends TextView, from TextView descends Button, and so on. Every widget in your layout inherits drawing, event handling, and measurement from View without those classes duplicating a line of that logic.
classDiagram
View <|-- TextView
TextView <|-- Button
Button <|-- AppCompatButton
AppCompatButton <|-- CustomButton
This hierarchy means you can extend any existing widget and inherit its complete behavior, then override or add exactly what you need.
Chapter 4: Building a Custom View by Extending AppCompatButton
When a standard Button does not meet your needs, you extend it. The subclass inherits all click-handling, layout, and drawing behavior, then layers on custom initialization and behavior.
1. Custom Button Class
// CustomButton.java
public class CustomButton extends androidx.appcompat.widget.AppCompatButton {
public CustomButton(Context context) {
super(context);
init();
}
public CustomButton(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public CustomButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
// Custom initialization code
setBackgroundColor(Color.BLUE);
setTextColor(Color.WHITE);
setText("Custom Button");
}
@Override
public boolean performClick() {
// Custom click behavior layered on top of the parent's click
Toast.makeText(getContext(), "Custom Button Clicked", Toast.LENGTH_SHORT).show();
return super.performClick();
}
}
Three constructors are provided because Android's inflation system calls different constructors depending on whether the view is created programmatically or inflated from XML. Each delegates to super(...) first — ensuring AppCompatButton's internals are fully set up — then calls init() for the custom styling. performClick is overridden to inject a toast, then calls super.performClick() so accessibility events and standard listener callbacks still fire.
2. Using CustomButton in a Layout
<!-- activity_main.xml -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.example.myapp.CustomButton
android:id="@+id/custom_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"/>
</RelativeLayout>
The fully qualified class name com.example.myapp.CustomButton tells the layout inflater which class to instantiate. Android calls the two-argument constructor (Context, AttributeSet) during inflation.
3. Activity Code
// MainActivity.java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
CustomButton customButton = findViewById(R.id.custom_button);
customButton.setOnClickListener(v -> {
// Additional behavior on click can also be added here
customButton.performClick();
});
}
}
findViewById returns the inflated CustomButton instance. The OnClickListener can add further logic at the call site while performClick still fires the in-class toast defined in CustomButton itself.
🧪 Try It Yourself
Task: Create a BaseActivity with a showToast helper, then build two subclasses — HomeActivity and ProfileActivity — each of which calls showToast from its onCreate with a screen-specific message.
Success criterion: Launch either activity in the emulator and see a toast appear with the correct screen name — no Toast.makeText call inside HomeActivity or ProfileActivity itself.
Starter snippet:
// BaseActivity.java
public class BaseActivity extends AppCompatActivity {
public void showToast(String message) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
}
}
// HomeActivity.java
public class HomeActivity extends BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
showToast("You are on Home"); // inherited — no import needed
}
}
Repeat the pattern for ProfileActivity. Add both to AndroidManifest.xml and navigate between them to watch each toast fire.
🔍 Checkpoint Quiz
Q1. What is the purpose of calling super.onCreate(savedInstanceState) inside an overridden onCreate method?
A) It compiles the layout XML file
B) It triggers the parent class's lifecycle setup, which Android requires
C) It registers the activity in the manifest
D) It inflates the view hierarchy automatically
Q2. Given this snippet, what happens when customButton.performClick() is called?
@Override
public boolean performClick() {
Toast.makeText(getContext(), "Custom Button Clicked", Toast.LENGTH_SHORT).show();
return super.performClick();
}
A) Only the toast shows; the standard click event is suppressed
B) A toast shows and then the parent's click handling (including registered listeners) fires
C) The method throws an exception because super.performClick() is deprecated
D) Nothing happens until setOnClickListener is also called on the instance
Q3. Why does CustomButton declare three constructors instead of one?
A) Java requires every class to have three constructors
B) Android's layout inflater selects different constructors for programmatic vs. XML creation
C) Each constructor handles a different screen density
D) AppCompatButton forces subclasses to declare all three
Q4. You are building a news app with twelve screens. Every screen must display a loading spinner and log analytics events on launch. How would you use inheritance to avoid duplicating that logic?
A1. B — Android's AppCompatActivity (and its parent chain) performs critical lifecycle wiring inside onCreate. Skipping super.onCreate leaves the activity in a broken state.
A2. B — The override adds the toast first, then return super.performClick() delegates to AppCompatButton's implementation, which dispatches click events to any registered OnClickListener and fires accessibility actions.
A3. B — The Android view inflation system calls the (Context, AttributeSet) constructor when it reads XML, while code that creates a view directly uses (Context). The third constructor (Context, AttributeSet, int) handles style attributes. Providing all three ensures the custom view works in every instantiation path.
A4. Create a BaseActivity extends AppCompatActivity with a showLoadingSpinner() method and an onLaunchAnalytics(String screenName) method. Have every screen activity extend BaseActivity and call those inherited methods from onCreate. This way the spinner and analytics logic live in exactly one place; changing them updates all twelve screens simultaneously.
🪞 Recap
- A subclass declared with
extendsinherits all public and protected fields and methods from its superclass, enabling reuse without duplication. super(...)delegates to the parent constructor;super.methodName()calls the parent's version of an overridden method — both are essential for preserving parent behavior.- Android Activities commonly share a
BaseActivitythat centralizes cross-cutting concerns like toasts, logging, or analytics. - Android's entire UI system is an inheritance hierarchy rooted at
View; every widget you use is already a subclass, and you can extend any of them to create custom components. - Overriding a method (e.g.,
performClick) lets a subclass specialize behavior whilesuper.performClick()ensures the parent's contract is still honored.
📚 Further Reading
- Java Inheritance — Oracle Docs — the source of truth on
extends,super, and method overriding in Java - Android View class reference — explore the root of Android's entire UI inheritance hierarchy
- Custom Views on Android — Android Developers — official guide to extending existing views with custom drawing and behavior
- ⬅️ Previous: Android Building Blocks IV: Content Providers
- ➡️ Next: Fragments, Intents & Intent Filters