Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Check if Elements in an Array are of a SubClass in Java

let's assume that I want to check if values within an Object array are of the superclass or subclass let's say for instance that my super class is Called Animal, I declare an array of type Animal

Animal myAnimals[] = new Animal[];

Now suppose that there are subclasses of Animal like Lion, Tiger, Elephant, etc. If I were to loop through an array, how would I differentiate the subclasses (Lion, Tiger, etc.) from the superclass Animal? Thanks!

like image 267
Linuxn00b Avatar asked Apr 02 '16 20:04

Linuxn00b


People also ask

How do you check if an object is a subclass Java?

The java “instanceof” operator is used to test whether the object is an instance of the specified type (class or subclass or interface). It is also known as type comparison operator because it compares the instance with type. It returns either true or false.

Are arrays a subclass of class array?

Arrays of more derived classes are subclasses of the corresponding array classes.

How do you check if a class is a subclass?

Python issubclass() is built-in function used to check if a class is a subclass of another class or not. This function returns True if the given class is the subclass of given class else it returns False. Parameters: Object: class to be checked.

How do you check if a class is a subclass of another class in Java?

If this Class object represents a primitive type, this method returns true if the specified Class parameter is exactly this Class object; otherwise it returns false . Reference: Class. isAssignableFrom(Class)


2 Answers

Using instanceof

At run time, the result of the instanceof operator is true if [...] the reference could be cast (§15.16) to the ReferenceType without raising a ClassCastException.

for (Animal a : myAnimals) {
    if (a instanceof Lion) System.out.println("Lion");
    // etc.
}
like image 57
Yassin Hajaj Avatar answered Oct 16 '22 15:10

Yassin Hajaj


As @YassinHajaj already said you can use the instanceof operator:

 for (Animal animal : animals) {

     if (animal instanceof Tiger) {

           Tiger tiger = (Tiger) animal;
     }
 }

However this results in unstructured code, besides this is hard to maintain. I personally prefer an more object oriented way.

As @JBNizet suggested in a comment you can use polymorphism. That's good but not perfect: you will need to change the implementation of all animal classes in order to add some logic.


Visitor pattern

However if you combine this with the visitor pattern. This will become very powerful because you can separate the logic into a visitor class:

interface AnimalVisitor {

    void visitLion(Lion lion);
    void visitFish(Fish fish);

    // Gets called when animal does not override the accept method:
    void visitDefault(Animal animal); 
}

class Animal {

    public void accept(AnimalVisitor visitor) {
        visitor.visitDefault(this);
    }
}

class Fish extends Animal {
    
    @Override
    public void accept(AnimalVisitor visitor) {
        visitor.visitFish(this);
    }
}

class Lion extends Animal {
    
    @Override
    public void accept(AnimalVisitor visitor) {
        visitor.visitLion(this);
    }
}

Then you can easily replace your loop with something like this:

AnimalVisitor visitor = new AnimalVisitor() {

    @Override
    public void visitLion(Lion lion) {
        // Do something with lion
    }
    
    @Override
    public void visitFish(Fish fish) {
        // Do something with fish
    }
}

for (Animal animal : animals) {
    animal.accept(visitor);
}

We can even take this one step further:

Reflective visitors

Since it is annoying to add for every different kind of animal an specific visitAnimal method, we can make use of reflection to avoid this! This will result in smaller and cleaner code:

interface Visitable { }

class Animal implements Visitable {

    // No accept method
}

class Fish extends Animal {}
class Goldfish extends Fish {}
class Shark extends Fish {}


// Take for example this visitor:

class BittenByVisitor extends ReflectiveVisitor {

    
    private Surfer surfer;

    public BitByVisitor(surfer) {
        this.surfer = surfer;
    }
        
    // We only care about the sharks:

    public void visit(Shark shark) {

        surfer.die();
    }


    // Any other fish is just the same:

    public void visit(Fish fish) {

        surface.decreaseHealthBy(1);
    }

    // It also works with interfaces:
    public void visit(VerySmallFish fish) {

        // Do nothing by purpose!
    }
}

And here an implementation of the reflective visitor:

abstract class ReflectiveVisitor {
    
    public void visit(Visitable v) throws NoSuchMethodException {
        Method m = findMethod(v);

        try {
            m.invoke(this, new Object[] { v });
        }

        catch ( IllegalAccessException e1 ) { /* code handling */ }
        catch ( InvocationTargetException e2 ) { /* code handling */ }
    }

    private Method findMethod(Visitable v) throws NoSuchMethodException {

        String methodName = "visit";
        Class visitable = v.getClass();

        while ( isAncestorOf("Visitable", visitable) {

            Class visitor = getClass();

            while ( isAncestorOf("Visitor", visitor) {

                try {
                    Method m = visitor.getDeclaredMethod(methodName, new Class[]{visitable});
                    return m;
                } catch ( NoSuchMethodException e ) {
                    visitor = visitor.getSuperclass();
                }
            }

            visitable = visitable.getSuperclass();
        }

        String errMsg = "put error message here";
        throw new NoSuchMethodException(errMsg);
    }

    private boolean isAncestorOf(String ancestorName, Class descendant) {

        try {
            return Class.forName(ancestorName).isAssignableFrom(descendant);
        }
        catch ( ClassNotFoundException e ) { /* code handling */ }
            return false;
        }
    }
}

Source code is taken from this paper.

I personally like to use a reflective visitor which avoids the Visitable class. You can define it like this:

class ReflectiveVisitor<T> {

    public void visit(T visitable);
}
like image 44
Tim Avatar answered Oct 16 '22 15:10

Tim