8

What is the best practice for referencing nested objects?

Say I have the following:

class Outer {
 private InnerA innerA;
 //getters and setters
}

class InnerA {
  private InnerB innerB;
  //getters and setters
}

class InnerB {
  private String someString;
  //getters and setters
}

and in my controller or service class I need to check the someString String variable of the InnerB class to make sure it is not null or not empty so I do this:

if (getOuter().getInnerA().getInnerB().getSomeString() != null && !getOuter().getInnerA().getInnerB().getSomeString().equalsIgnoreCase("") {
  //do something
}

To me this looks messy and could have issues if the nested objects themselves are null.

Do I create getters ans setters in the parent objects for the child objects checking for null? Just wondering what the best practice was if any and/or what some of you do in your code?

blong824
  • 3,920
  • 14
  • 54
  • 75

11 Answers11

8

If any of those objects can be null, then you have to check for null before calling a getter on this object, of course.

But this kind of chaining is a bad smell of a lack of encapsulation (anemic objects having just data, and no behavior). You're violating the law of Demeter : don't talk to strangers.

JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • 3
    so, `PersonDAO.getPerson(123).getAddress().getCity().loadMap()` is a mistake? – Nishant Mar 09 '11 at 16:37
  • JB Nizet can you comment on Nishant question please? – blong824 Mar 09 '11 at 17:57
  • It's a smell, it's not a proof. And the law of Demeter is a principle, which is often good to follow, but not necessarily always. It has advantages and disadvantages. In the case of Nishant example, you could have a loadMap() directly in Person, delegating to its address. It could even implement a Mappable interface. The example of the OP is a bit different, because all it does is access to data. – JB Nizet Mar 09 '11 at 18:25
8

You can use Apache Commons BeanUtils to navigate through your nested properties like this:

Add method getSomeString() to your Outer class and write something like

PropertyUtils.getNestedProperty(this, "innerA.innerB.someString");

I can't remember if that PropertyUtils class check null properties, but I would look Apache Commons BeanUtils site.

Hope this helps!

kosmoplan
  • 487
  • 1
  • 3
  • 8
5

I would recommend reading up on the Law of Demeter.

John Cromartie
  • 4,184
  • 27
  • 32
  • 2
    I would recommend become familiar with the wonderful world of XML documents mapped into POJOs. Just an example where LoD is not applicable at all. – Pavel Vlasov Jun 01 '16 at 11:20
4

You have two options:

  1. Use Null Object design pattern
  2. Wait for Java 7 Java 8 null safe operator.
Nishant
  • 54,584
  • 13
  • 112
  • 127
3

In Java 8+, this can be handled by using Optional.

String value = Optional.ofNullable(outer).map(x -> x.getInnerA())
                      .map(x -> x.getInnerB()).map(x -> x.getSomeString())
                      .orElse(DEFAULT_VALUE);
Unmitigated
  • 76,500
  • 11
  • 62
  • 80
1

This is a java limitation. You should implement helper methods in the parent "OuterObject" if it helps you to reduce code duplication.

This helper methods are useful for object which are aggregating other object and you need to check only if the nested value exists.

Code:

getOuter().hasInnerB();

Which would do all the null checks.

This problem occurs often with objects generated from *.xsd. In a complicated xml structure it happens often that there a many nested optional nodes. And what usually is interesting is the last node. Then its better to write helper methods which will answer the questions if node exists for reuse.

If it comes up to your cod sample i usually write something like that

if (hasSomeString(getOuter())) {
  //do something
}
wmlynarski
  • 516
  • 5
  • 8
1

I don't think users of Outer should have knowledge of Outer.InnerA.InnerB.SomeString - it's buried to deep. You can't change implementation of InnerB without touching clients of Outer 3 levels separated - so what's the point of even having inner classes? Situations like you're describing are ugly and shouldn't arise.

I'd recommend you first consider whether SomeString belongs in InnerB or InnerA or Outer.

Now suppose your hierarchy is correct, yet SomeString has this unique property of being required by clients of Outer (if SomeString is not unique that way, hierarchy is definitely wrong). In this case, Outer.getSomeString(), or better yet, Outer.isSomeStringNullOrEmpty(), so that at least clients of Outer don't have to know about InnerA and InnerB

PS. someString.equalsIgnoreCase("") is expensive, don't use that. Much cheaper is someString.length() == 0

iluxa
  • 6,941
  • 18
  • 36
1

It's messy, but if you only had to do it in one place then I might live with it. Otherwise, I'd implement a Outer.getSomeString() that hides the internal path, since your Outer class is the one you're exposing as your interface.

This also allows you to deal with a case where one of the intermediary inner classes is null, without having to do a number of sequential checks each time you try to access someString.

Kendrick
  • 3,747
  • 1
  • 23
  • 41
1

My belief is that you should not expose "inner-inner" members through methods of the outer class unless you are adding some sort of functionality or different behaviour to them or the value is "essential" to the use of the outer class. However this is also a matter of judgement and can vary depending on the use and architecture of your code.

On a side note if you want the code for a long string of invocations to be "less ugly" I suggest voting for addition of the Elvis Operator for project coin in the next version of Java (I wish it had made it into 7 :-().

M. Jessup
  • 8,153
  • 1
  • 29
  • 29
0

I've written a Java 8 Method:

public class Helper {
    public static <IN, OUT> OUT returnNullOrCallFunction(IN o, Function<IN, OUT> f) {
        return o == null ? null : f.apply(o);
    }
}

Now you can call:

Helper.returnNullOrCallFunction(
        myObject.getSomeOtherObject(),
        SomeOtherObject::toString
);

If myObject.getSomeOtherObject() is null the method will return null, else it will call myObject.getSomeOtherObject().toString().

That's pretty useful, if you just have to go one level deeper.

For multi level, it gets ugly:

Helper.returnNullOrCallFunction(
        Helper.returnNullOrCallFunction(
                myObject.getSomeOtherObject(),
                SomeOtherObject::getAnotherObject
        ),
        AnotherObject::toString
);
Benjamin M
  • 23,599
  • 32
  • 121
  • 201
0

If you are already using Spring, consider BeanWrapper or ConfigurablePropertyAccessor:

BeanWrapper propertyAccessor = PropertyAccessorFactory.forBeanPropertyAccess(outer);
String someString = propertyAccessor.getPropertyValue("innerA.innerB.someString");

getPropertyValue throws an Exception if any of the nested properties is null. you can catch that Exception and return whatever you want.

Mahmoud
  • 9,729
  • 1
  • 36
  • 47