DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Enterprise AI Trend Report: Gain insights on ethical AI, MLOps, generative AI, large language models, and much more.

2024 Cloud survey: Share your insights on microservices, containers, K8s, CI/CD, and DevOps (+ enter a $750 raffle!) for our Trend Reports.

PostgreSQL: Learn about the open-source RDBMS' advanced capabilities, core components, common commands and functions, and general DBA tasks.

AI Automation Essentials. Check out the latest Refcard on all things AI automation, including model training, data security, and more.

Related

  • Achieving Inheritance in NoSQL Databases With Java Using Eclipse JNoSQL
  • Java 22 Brings Major Enhancements for Developers
  • The Power of LLMs in Java: Leveraging Quarkus and LangChain4j
  • How To Get Started With New Pattern Matching in Java 21

Trending

  • Python for Beginners: An Introductory Guide to Getting Started
  • Data Flow Diagrams for Software Engineering
  • Running LLMs Locally: A Step-by-Step Guide
  • Enhancing Secure Software Development With ASOC Platforms
  1. DZone
  2. Coding
  3. Java
  4. Problems With Inheritance in Java

Problems With Inheritance in Java

Having trouble with proper subclass inheritance in Java? Click here to figure out how to avoid broken code.

By 
Yogen Rai user avatar
Yogen Rai
·
Jul. 10, 18 · Tutorial
Like (12)
Save
Tweet
Share
34.0K Views

Join the DZone community and get the full member experience.

Join For Free

Inheritance is one of the fundamental principles in an object-oriented paradigm, bringing a lot of values in software design and implementation. However, there are situations where even the correct use of inheritance breaks the implementations. This post is about looking at them closely with examples.

Fragility of Inheritance

The improper design of the parent class can leads subclasses of a superclass to use the superclass in unexpected ways. This often leads to broken code, even when the IS-A criterion is met. This architectural problem is known as the fragile base class problem in object-oriented programming systems.

The obvious reason for this problem is that the developer of a base class has no idea of the subclass design. When they modify the base class, the subclass implementation could potentially break.

For example, the following program shows how seemingly an inheriting subclass can malfunction by returning the wrong value.

class Rectangle {
    private int length;
    private int breadth;

    public Rectangle(int length, int breadth) {
        this.length = length;
        this.breadth = breadth;
    }

    public int calculateArea() {
        return length * breadth;
    }

    // getters and setters
}

// Square IS-A Rectangle
class Square extends Rectangle {
    public Square(int side) {
        super(side, side);
    }
}


Now, the following shows how to the test to ensure that the inheritance works fine. 

@Test
public void testSquare() {    
  Square square = new Square(5);    
  assertEquals("Area of square", 25, square.calculateArea());   // fine

  // set breadth of square :(    
  square.setBreadth(9);    
  assertEquals("Area of new square", 81, square.calculateArea());  // ohh nooo
}


Obviously, if I create the instance of Square  and call a method calculateArea, it will give the correct value. But, if I set any of dimension of the square, since the square is a rectangle, it gives the unexpected value for the area and the second assertion fails as below:

java.lang.AssertionError: Area of new square 
Expected :81
Actual :45


Is There Any Solution?

There is no straightforward solution to this problem because this is all about following the best practices while designing architecture. According to Joshua Bloch, in his book Effective Java, programmers should "design and document for inheritance or else prohibit it."

If there is a breakable superclass, it is better to prohibit inheritance by labeling a declaration of a class or method, respectively, with the keyword "final." And, if you are allowing your class to be extended, it is best to only use one way to populate fields.

Here, use either constructors  or setters  but not both.

So, if I remove the setters from the parent class as below:

class Rectangle {
    private int length;
    private int breadth;

    public Rectangle(int length, int breadth) {
        this.length = length;
        this.breadth = breadth;
    }

    public int calculateArea() {
        return length * breadth;
    }

    // getters
}


Then, the child classes can't misuse the setter avoiding fragility issue as:

@Test
public void testSquare() {    
  Square square = new Square(5);    
  assertEquals("Area of square", 25, square.calculateArea());   // fine

  // square.setBreadth(9);    //no way to set breadth :)
}


Inheritance Violates Encapsulation

Sometimes, your private data gets modified and violates encapsulation. This will happen if you are extending features from an undocumented parent class — even though the IS-A criterion is met.

For example, let us suppose A overrides all methods in B by first validating input arguments in each method (for security reasons). If a new method is added to B and A  and is not updated, the new method introduces a security hole.

For example, I have created new HashSet implementation to count the numbers of elements added to it as:

class MyHashSet<T> extends HashSet<T> {
    //The number of attempted element insertions since its creation --
    //this value will not be modified when elements are removed
    private int addCount = 0;

    public MyHashSet() {}

    @Override
    public boolean add(T a) {
        addCount++;
        return super.add(a);
    }

    @Override
    public boolean addAll(Collection<? extends T> c) {
        addCount += c.size();
        return super.addAll(c);
    }

    public int getAddCount() {
        return addCount;
    }
}


Everything looks good. So, it is time to test this extension!

@Test
public void testMyHashSet() {    
  MyHashSet<String> mhs = new MyHashSet<>();    
  mhs.add("A");    
  mhs.add("B");    
  mhs.add("C");    
  assertEquals("Number of attempted adds so far", 3, mhs.getAddCount());

  mhs.remove("B");    
  assertEquals("Number of attempted adds so far even after removal", 3, mhs.getAddCount());  

  mhs.addAll(Arrays.asList("D", "E", "F"));    
  assertEquals("Size of Elements in current set", 5, mhs.size());    
  assertEquals("New number of attempted adds so far", 6,  mhs.getAddCount());  // fails
}


The test fails with a failure in the last assertion as below:

java.lang.AssertionError: New number of attempted adds so far 
Expected :6
Actual :9
Inheritance


The cause of the problem is that in the implementation of  HashSet, addAll  calls the add  method. Therefore, we are incrementing  addCount  too many times in calls to  addAll. 

How to Fix This Issue?

The principle is the same as in an earlier fix: "Design and document for inheritance or else prohibit it." The proper documentation while designing features would reduce the chances of issues like this. 

Fix specific to this issue is not to increment  addCount  in  addAll  operations, since the value is getting updated in add  operation, which gets called from  addAll  as:

class MyHashSet<T> extends HashSet<T> {
    // . . .
    @Override
    public boolean addAll(Collection<? extends T> c) {
        return super.addAll(c);
    }
    // . . .
}


So, this is it! Until next time, happy learning!

As usual, all the source code presented in the above examples is available on GitHub.

Inheritance (object-oriented programming) Java (programming language)

Opinions expressed by DZone contributors are their own.

Related

  • Achieving Inheritance in NoSQL Databases With Java Using Eclipse JNoSQL
  • Java 22 Brings Major Enhancements for Developers
  • The Power of LLMs in Java: Leveraging Quarkus and LangChain4j
  • How To Get Started With New Pattern Matching in Java 21

Partner Resources


Comments

ABOUT US

  • About DZone
  • Send feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends: