Skip to main content

2 posts tagged with "java"

View All Tags

Crossing abstraction barrier between parent and child class

· 5 min read

Motivation

This article is inspired by a question I received in a programming methodology class. In this class, in which we write Java code to solve programming exercises, we have the constraint that every attribute of a class should be private and final. It means there is no access to the field outside of the class, and no modification is allowed once this field is initialized. This strict requirement is put in place to enforce immutability when constructing a class object in Java.

Sooner or later, when the exercises get more complex, we tend to move on to an OOP solution whereby multiple classes are constructed and organized with the help of inheritance. The problem then arises when there is a need to access this private final field in the parent class from a subclass. What should we do then?

To give a concrete example, let's say we have the following classes:

class Parent {
private final int value;

Parent(int value) {
this.value = value;
}
}

class Child extends Parent {
Child(int value) {
super(value);
}

int add(int another) {
return super.value + another; // UNABLE TO ACCESS!
}
}

What should we do if the child class wants to access value from the parent?

Solutions

Change modifier

The simplest way to deal with that is to change the access modifier from private to something else - perhaps public or protected. This solution can be legitimate depending on the context. In some cases, perhaps it is perfectly normal to expose this value to other classes.

Add a getter method

From the Oracle's Java tutorial on inheritance

A subclass does not inherit the private members of its parent class. However, if the superclass has public or protected methods for accessing its private fields, these can also be used by the subclass.

So, another possible solution is to have a getter method in the parent class and make that method public. This way child classes (and technically other classes) will have access via the getter. So a quick example will be:

class Parent {
private final int value;

Parent(int value) {
this.value = value;
}

public int getValue() {
return this.value;
}
}

class Child extends Parent {
Child(int value) {
super(value);
}

int add(int another) {
return super.getValue() + another; // CAN ACCESS!
}
}

Having a getter method can be beneficial in the sense that even though now a "private" field is exposed, you still have one layer of abstraction over it. The users of the getter method do not need to know how that value is generated, which can be manipulated (if needed) by some complex preprocessing steps in the getter method. Also, the underlying private field could change drastically and yet the users of the getter method are unaware.

Rethink code design

Lastly, this problem may be a signal to rethink if there is a legitimate need to access a private final field. Given a parent-child relationship, sometimes it's difficult to be clear about which field/method should reside in which classes.

  • Would it be better to have the field in the child class instead?
  • Can we shift what the child class wanted to do with value into the parent class as a general method that the child class can inherit and possibly override?

A better code design might suggest that the private final field can stay as is, maintaining an abstraction barrier between the parent and the child class. One example solution is then:

class Parent {
private final int value;

Parent(int value) {
this.value = value;
}

int add(int another) {
return this.value + another;
}
}

class Child extends Parent {
Child(int value) {
super(value);
}

int add(int another) { // will work if this method is omitted as well,
return super.add(another); // as it will be inherited
}
}

Anti pattern

A problematic walkaround that some might come up with is to redeclare the same field in the child class.

class Parent {
private final int value;

Parent(int value) {
this.value = value;
}
}

class Child extends Parent {
private final int value;

Child(int value) {
super(value);
this.value = value;
}

int add(int another) {
return this.value + another; // will work but not recommended
}
}

This works but is arguably a bad design because it does not make use of inheritance to reduce any duplicates between shared properties. It also could result in the values (that meant to represent the same thing) going out of sync, especially if these fields were not declared as final.

Conclusion

When I was asked the motivating question, my immediate response was: "make a public getter method". To which I was then asked a follow-up question:

  • Why do we resort to using a public getter method, when we want to keep the field private?

Which got me thinking:

  • Why can't private fields be inherited?

This article is a reminder for me to ask the "why" questions more often, and explore the reasons for the answers.

Explaining Java's Optional

· 3 min read
TLDR: You should watch this video by Stuart Marks if you wish to learn more about the considerations behind Java's Optional feature and its practical usage.

Motivation

I was introduced to Java's Optional as a response to the billion-dollar mistake made by Tony Hoare, the little monster called "null".

Tony Hoare introduced Null references in ALGOL W back in 1965 "simply because it was so easy to implement", says Mr. Hoare. He talks about that decision considering it "my billion-dollar mistake".

Honestly, I had never considered null as the problematic kid on the block. If there were any thoughts as a Java beginner, I felt that null was extremely helpful in dealing with return types. No suitable object for a method to return? Just return null.

When I learned about Optional, I thought that it is an unnecessary abstraction. Why go through the trouble of boxing up an object and then pass the instructions to the box in order to interact with the value contained in the Optional? This is actually because of the problem of null reference and also issues with having null as a return value.

Problem with null reference

When methods calls are chained together, a method within the chain will cause the following method to throw an exception if it returns null.

methodA().methodB().methodC();
// if methodB returns null, it results in
// null.methodC()
// Not a good idea

The issue with this is that although we can proactively check for null references before invoking a method call, that solution will result in messy and necessarily long code just to ensure a method call is legitimate.

Foo foo = methodA();
if (foo != null) {
Bar bar = foo.methodB();
if (bar != null) {
Coo coo = bar.methodC();
}
}

Problem with the meaning of null

There might be cases where null is returned to signify an empty value. And there might be cases that null is returned to mean invalid. Therefore, it might be confusing to use it as a return type. This often means that the developer has to put in an extra effort to express his intent in the comment or in the documentation. The callers to such code also suffer from having to deal with such functions with care.

Solution provided by Optional

I choose to leave out the details of Optional API and its usage for now as I intend to cover it together with Java's Stream and CompletableFuture, drawing on their similarity of providing a context/abstraction. The details of the Optional API could be found at the official Java API website

Conclusion

Optional is simply a language feature but it is worth learning the concept and reasoning behind its existence. This is because most languages face the same issue and while individual implementations might have different names, they all operate on similar logic and resolution technique.

Recommended guidelines

  • Optional intended to provide is a limited mechanism for method return types where there is a clear need to represent "no result", and where using null for that is overwhelmingly likely to cause errors.
  • Avoid using Optional.get()
  • Avoid using Optional in fields, method parameters, and collections.