A container may contain bikes and chairs, both belonging to a person. I would like to check, if the container contains either bikes or chairs of said person. Is this possible without using instanceof
?
public class Container {
public Map<Person, List<Item>> items = new HashMap<>();
public void add(Person p, Item item) {
items.get(p).add(item);
}
public boolean containsChair(Person owner) {
for(Item i : items.get(owner)) {
if(i instanceof Chair) {
return true;
}
}
return false;
}
public boolean containsBike(Person owner) {
for(Item i : items.get(owner)) {
if(i instanceof Bike) {
return true;
}
}
return false;
}
}
For the purpose of illustration, Item, Bike, Chair, Person are all simplest class stubs:
public class Person { public String name; }
public abstract class Item {}
public class Bike extends Item { public Wheel[] wheels;}
public class Chair extends Item { public Leg[] legs;}
public class Wheel {}
public class Leg {}
In the runner, a Person should be able to add Chairs and Bikes to its container:
import java.util.ArrayList;
public class Runner {
public static void main(String[] args) {
Container c = new Container();
Person p = new Person();
// Prevent null pointer exception
c.items.put(p, new ArrayList<>());
c.add(p, new Chair());
// True
System.out.println(c.containsChair(p));
}
}
You could add to class Item
an abstract method ItemType getType()
. ItemType
would be an enum enumerating all possible item types.
public abstract class Item {
public abstract ItemType getType();
}
public enum ItemType {
BIKE, CHAIR;
}
Implementation of Chair
:
public static class Chair extends Item {
public Leg[] legs;
@Override
public ItemType getType() {
return ItemType.CHAIR;
}
}
Then you could define a contains
method to search for a the given Person
if it has an item with a certain ItemType
:
public boolean contains(Person owner, ItemType itemType) {
return items.get(owner).stream().anyMatch(item ->itemType.equals(item.getType()));
}
Or null-safe regarding the owner
s items
list:
public boolean contains(Person owner, ItemType itemType) {
return Optional.ofNullable(items.get(owner))
.map(i -> i.stream().anyMatch(item -> itemType.equals(item.getType())))
.orElse(false);
}
Usage:
public static void main(String[] args) {
Container c = new Container();
Person p = new Person();
// Prevent null pointer exception
c.items.put(p, new ArrayList<>());
c.add(p, new Chair());
// True
System.out.println(c.contains(p, ItemType.CHAIR));
}
EDIT
Following this approach there is no need for instanceof
checks. The usage of instanceof
can be a hint indicating that the design has some flaws.
You can store Bike
and Chair
in two different datastructure.
public final class Container {
private final Map<Person, List<Chair>> chairs = new HashMap<>();
private final Map<Person, List<Bike>> bikes = new HashMap<>();
public void add(Person p, Chair chair) {
chairs.putIfAbsent(p, new ArrayList<Chair>());
chairs.get(p).add(chair);
}
public void add(Person p, Bike bike) {
bikes.putIfAbsent(p, new ArrayList<Bike>());
bikes.get(p).add(bike);
}
public boolean containsChair(Person owner) {
return chairs.getOrDefault(owner, Collections.emptyList()).size() > 0;
}
public boolean containsBike(Person owner) {
return bikes.getOrDefault(owner, Collections.emptyList()).size() > 0;
}
}
Note that I also made your instance fields private
to hide the fact that data is stored in a Map
and avoid the runner code to have the responsibility to instanciate an ArrayList
if not existant. Both the class and its fields are also final
to achieve a better immutability. Both encapsulation and immutability are considered good practices when doing OOP.
Usage
public static void main(String[] args) {
Container c = new Container();
Person p = new Person();
c.add(p, new Chair());
System.out.println(c.containsChair(p)); //true
System.out.println(c.containsBike(p)); //false
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With