I am learning the basics of Java and I am exploring Optionals and abstract classes
so I came across the following issue,
I have this code
import java.util.Optional; public abstract class Animal { abstract void makeSound(); public static void main(String[] args) { System.out.println("Start"); Dog dog = new Dog(); Cat cat = new Cat(); Horse horse = new Horse(); Animal[] animals = {dog, cat, horse}; for (Animal animal : animals) { Optional<Dog> _dog = animal instanceof Dog ? Optional.of((Dog) animal) : null; Optional<Cat> _cat = animal instanceof Cat ? Optional.of((Cat) animal) : null; Optional<Horse> _horse = animal instanceof Horse ? Optional.of((Horse) animal) : null; if (_dog.isPresent()) { System.out.println("it is a Dog"); } else { System.out.println("it is NOT a Dog"); } animal.makeSound(); } } } class Horse extends Animal { String speed = "Fast"; @Override void makeSound() { System.out.println("Neighing..."); } } class Dog extends Animal { String color = "Brown"; @Override void makeSound() { System.out.println("Barking..."); } } class Cat extends Animal { Integer lives = 9; @Override void makeSound() { System.out.println("Mewoing......"); } }
I was expecting to see the prints on the console “It is a Dog” followed by 2 “It is not a Dog” Since I’m using the method .isPresent()
on optionals,
But I got 1 print and then a NullPointerException
:
That’s what I got printed:
Start it is a Dog Barking... Exception in thread "main" java.lang.NullPointerException at com.example.helloworldeclipse.Animal.main(Animal.java:24)
Shouldn’t isPresent be safe? is there a better way to cast abstract classes to subclasses in situations similar like this?
I don’t get why it’s not working.. What am I doing wrong here?
Thank you in advance for all the answers..
Advertisement
Answer
The problem is that you are assigning null
to an Optional
reference when the instanceof
check fails.
You have at least two options:
Use
Optional.empty()
instead ofnull
Optional<Dog> dog = animal instanceof Dog ? Optional.of((Dog) animal) : Optional.empty();
Use Optional’s
filter
andmap
methods:Optional<Dog> dog = Optional.of(animal) .filter(Dog.class::isInstance) .map(Dog.class::cast);
The predicate
Dog.class::isInstance
checks if the given instance, which is the value within theOptional
, is an instance ofDog
. This isequivalent
toinstanceof
. ThenDog.class::cast
casts the given object to aDog
instance.
Note: If animal
itself could be null
, then you should use Optional::ofNullable
instead of Optional::of
.