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..
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 of null
Optional<Dog> dog = animal instanceof Dog ? Optional.of((Dog) animal) : Optional.empty();
Use Optional's filter
and map
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 the Optional
, is an instance of Dog
. This is equivalent
to instanceof
. Then Dog.class::cast
casts the given object to a Dog
instance.
Note: If animal
itself could be null
, then you should use Optional::ofNullable
instead of Optional::of
.