Java – Checking for the presence of optional properties of an object

api-designjava

I'm designing a class that holds several types of data. Some of the properties are optional. For example, let's say I have a class that represents a person, and one of the properties is occupation. I have another property, military rank, but this property is can only be retrieved if the person's occupation is military. What's the best way to handle this?

  1. Have getMilitaryRank() throw IllegalStateException if the occupation isn't military
  2. Have getMilitaryRank() return an Optional<Rank>
  3. Return null if the occupation is invalid (I don't like nulls)
  4. Something else?

Idea 1 works because the user can check the occupation prior to calling this method, making sure they don't call it on an invalid person.

Idea 2 seems unnecessary, because you can infer whether the Optional is present by calling getOccupation().

However, let's say my Person class has another optional property, but its presence can't be inferred from the value of another property; for example, a middle name. Obviously it's best for getMiddleName() to return an empty Optional or null if the person has no middle name, as the presence of a middle name can't really be inferred some other way.

Since I should probably be consistent in how I'm handling optional properties, it seems like I could do one of the following:

  1. Use Optional for any property that may be absent, even if it can be inferred from another property.
  2. Add methods like hasMiddleName() to resolve the discrepancies (this seems like a bad idea)
  3. Not be consistent and let the properties whose presence can be determined externally throw an exception, while the ones that are independent be wrapped in an Optional.
  4. Something else?

While nulls could simplify things, it causes a problem if an optional property is an int, which can't be nullable in Java. I could use an Integer, but that seems like a bad idea.

What is the best way to handle these different types of optional properties?

Best Answer

There's two solutions, depending on the specifics of your problem. If these optional properties are only combined in a relatively small number of ways, you can use a sum type to distinguish between them. The advantage of this approach is that you can guarantee exhaustiveness - you can statically ensure that wherever a decision must be taken based on the particular kind of person, all of the kinds are handled somehow.

The other solution is to group the optional properties that always occur together into interfaces and then implement the interfaces you can support in each class. You can detect the particular interfaces a person supports by applying instanceof and then casting. This approach won't force you to update all the use sites when you add a new interface, but works better when the number of possible combinations of optional properties is very large. E.g. if there's 7 groups of optional properties, there's 127 ways to combine those groups into classes, and it'd be ridiculous to take a decision based on each possible combination. If you need to reuse the implementations of those interfaces, use composition.

That aside, do be careful about making assumptions about people's names.

Related Topic