the1gofer
the1gofer

Reputation: 77

Storing varied object types with order in Java

I am trying to model a solar system in Java, and need to store different random amounts of objects types and preserve their order somehow. I am currently using the following class structure:

class PlanetarySystem - currently has a list of each object type (below)

class planetaryBody
         class star extends planetaryBody
         class planet extends planetaryBody
         class belt extends planetaryBody
         class disk extends planetaryBody

For example, lets say I want to loop through the system show the details of the PlaneitaryBody objects. The stats for the Star would probably be different than a Planet, and so on. Here is a simple example.

public class PlanetaryBody
{
     private String name;

     public PlanetaryBody()
     {
          this.name = "Generic Name";
     }

     public String getName()
     {
          return this.name;
     }
}

public class Star extends PlanetaryBody
{
     private int temperature;

     public Star()
     {
          this.temperature = 12345;
     }
     public String getTemp()
     {
          return this.getTemp();
     }

}

Now if I later create something like

List<planetaryBody> system = new List<planetaryBody>();
//then put some stars, planets, disks, etc in.
system.get(x).getName() //will work
system.get(x).getTemp() //will not work because type is planetaryBody

later I want to loop through the list and get lets say the name of everything, the temperature of the star, the size of the belt, the color of the planet, etc. I don't think I will be able to pull out specific function for the subclasses. That's the problem I am trying to solve.

Can I create something that allows me to store the objects in order, retain what they are, and then do something specific to that class later?

I was thinking of trying to use a map tree, but I think I would have to use it with the planetaryBody type. That creates problems later when I am trying to pull the objects out later and they come out as planetaryBodies. I've read that doing if statements to figure out what class they are isn't the best OOP practice, and honestly I don't even know if that would work as intended.

I'm new and learning, so examples, and further reading is appreciated.

edit: more clarification.

Upvotes: 1

Views: 116

Answers (1)

Anonymous
Anonymous

Reputation: 86252

There are many solutions to your problem, each with their pros and cons, and also many opinions. I just wanted to present an old trick, here combined with a modern Optional. Superclass:

public abstract class PlanetaryBody {

    /**
     * @return If this PlanetaryBody is a Star, then an Optional containing this as a Star.
     * Otherwise an empty Optional.
     */
    public Optional<Star> asStar() {
        return Optional.empty();
    }

    public Optional<Planet> asPlanet() {
        return Optional.empty();
    }

    // Similar methods for other subclasses

}

Example subclass:

public class Star extends PlanetaryBody {

    public void shineBrightly() {
        // ...
    }

    @Override
    public Optional<Star> asStar() {
        return Optional.of(this);
    }

}

The above allows us to use the classes like this:

    PlanetaryBody body = // ...

    body.asStar().ifPresent(s -> {
        // body is a star
        s.shineBrightly();
    });

Pros:

  • Flexible in that you can handle planetary bodies uniformly where appropriate and easily convert to concrete type where needed.
  • Doesn’t use instanceof or other frowned-upon constructs.
  • Type safe.

Cons:

  • The classes are tightly coupled. The superclass needs to know its subclasses. You need to modify the superclass every time you add a subclass.
  • It may be considered a bit wordy, and you need to get very similar code right in all subclasses.

Edit: I am using the Optional class introduced in Java 8. The main (only?) purpose is null-safe return values from methods. When a method may return something or nothing — as asStar may, for example — the old way would be to have it return a reference that could potentially be null. And this gave rise to NullPointerExceptions when the caller wasn’t aware that the return value could be null or just forgot to check. Happened very often. You may argue that in this case it’s not a great risk, so we could live with returning either this as a Star or null.

With Optional the method needs never (and must never) return null. Instead it returns an Optional that either has a value in it or not. The way I usually use an Optional I get from a method is through its isPresent method as shown above. I pass a lambda (or method reference) to it, and the code inside is only executed if a value was present. So in my code s is a reference to the Star inside the Optional and is guaranteed to be non-null (otherwise the code wouldn’t be executed at all).

Java 9 introduced the variant ifPresentOrElse that will also execute a code block in the case where the Optional is empty.

You can of course read much more about Optional on the Internet. I’ve become quite fond of it. I recommend the slide deck in the link below if you can read it out of context of the talk — otherwise the talk is also available on YouTube. One word of caution: If you read somewhere that you should use the methods Optional.isPresent and Optional.get, don’t believe that, you very rarely should (note the difference between isPresent with s and ifPresent with f, both methods exist, use the latter as I do).

Variant that doesn’t use Optional (not tested). In the superclass:

    public Star asStar() {
        return null;
    }

In the concrete subclass:

    public Star asStar() {
        return this;
    }

How to use:

    if (body.asStar() != null) {
        // body is a star
        body.asStar().shineBrightly();
    }

Link: Slide deck Optional: The Mother of All Bikesheds by Stuart Marks, Oracle

Upvotes: 1

Related Questions