Reputation: 77
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
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:
instanceof
or other frowned-upon constructs.Cons:
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 NullPointerException
s 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