Passing a list of base class objects to a method and using instanceof to filter them

I'm working on a Java reverse engineering project, where I am analyzing bytecode and trying to identify classes, methods and fields using the ASM framework. After identifying these I transform some classes to implement my custom interfaces with, e.g., getter methods. For identifying classes, I have created an abstract class Analyzer with an abstract method for running the analysis on a collection of classes. Child classes of Analyzer try to identify a certain class/field/method of interest. For instance, AnimalAnalyzer attempts to identify the class which represents an animal. After running the analyzers, I inject my own interfaces and their implementations into the classes. Similarly to Analyzer, I have created an Injector interface with an inject(List analyzers) method. For each class that I want to inject there is a separate class which implements the interface, e.g., AnimalInjector. The injector iterates the list of Analyzers, which contain the results of their analysis, and uses the relevant ones by checking their type with the instanceof operator. By "relevant ones" I mean the analyzers which contain the information needed to generate the bytecode with. public abstract class Analyzer { public abstract void run(ClassCollection classCollection); } public class AnimalAnalyzer extends Analyzer { private ClassNode identifiedClass = null; @Override public void run(ClassCollection classCollection) { for (ClassNode classNode : classCollection.getAllClassNodes()) { boolean isMatch = doesClassMatch(classNode); // method omitted from example if (isMatch) { identifiedClass = classNode; break; } } } } public interface Injector { void inject(List analyzers); } public class AnimalInjector implements Injector { @Override public void inject(List analyzers) { AnimalAnalyzer animalAnalyzer = null; for (Analyzer analyzer : analyzers) { if (analyzer instanceof AnimalAnalyzer) { animalAnalyzer = (AnimalAnalyzer) analyzer; } } if (animalAnalyzer == null) { throw new IllegalStateException("AnimalAnalyzer not found in list"); } ... (bytecode generation and injection) } } One of the reasons that I chose to do it this way is that the analyzers have to be run in a specific order. For example, to identify a Dog which extends Animal, the latter has to be found first. For this purpose I created MasterAnalyzer, which stores a list of the analyzers in a specific order. When run is invoked on this analyzer, it calls run on the analyzers in the list in the right order. public class MasterAnalyzer extends Analyzer { private List analyzers = new ArrayList(); public MasterAnalyzer() { analyzers.add(new AnimalAnalyzer()); analyzers.add(new DogAnalyzer()); ... (more analyzers) } public List getAnalyzers() { return analyzers; } @Override public void run(ClassCollection classCollection) { for (Analyzer analyzer : analyzers) { analyzer.run(classCollection); } } } The injectors, in turn, can all be run with the same command: inject(masterAnalyzers.getAnalyzers()). While this makes it easy at the client side, it causes the injectors to know more than they need to know. For instance, let's assume that we also have a CatAnalyzer. Now, when we run the DogInjector, the list of analyzers will include the CatAnalyzer, even though the DogInjector does not care about it. This, along with the nasty instanceof usages, causes red signals to me. Now I am reaching out to you more experienced developers for some tips on how to improve my design. While searching for alternative ways, I found the Visitor pattern, which could be applicable here. However, I am still unsure how to apply it and whether it would improve my design or not.

Feb 2, 2025 - 11:43
 0
Passing a list of base class objects to a method and using instanceof to filter them

I'm working on a Java reverse engineering project, where I am analyzing bytecode and trying to identify classes, methods and fields using the ASM framework. After identifying these I transform some classes to implement my custom interfaces with, e.g., getter methods.

For identifying classes, I have created an abstract class Analyzer with an abstract method for running the analysis on a collection of classes. Child classes of Analyzer try to identify a certain class/field/method of interest. For instance, AnimalAnalyzer attempts to identify the class which represents an animal.

After running the analyzers, I inject my own interfaces and their implementations into the classes. Similarly to Analyzer, I have created an Injector interface with an inject(List analyzers) method. For each class that I want to inject there is a separate class which implements the interface, e.g., AnimalInjector. The injector iterates the list of Analyzers, which contain the results of their analysis, and uses the relevant ones by checking their type with the instanceof operator. By "relevant ones" I mean the analyzers which contain the information needed to generate the bytecode with.

public abstract class Analyzer {    
    public abstract void run(ClassCollection classCollection);
}

public class AnimalAnalyzer extends Analyzer {
    private ClassNode identifiedClass = null;
    @Override
    public void run(ClassCollection classCollection) {
        for (ClassNode classNode : classCollection.getAllClassNodes()) {
             boolean isMatch = doesClassMatch(classNode); // method omitted from example
             if (isMatch) {
                 identifiedClass = classNode;
                 break;
             }
        }
    }
}
public interface Injector {
    void inject(List analyzers);
}

public class AnimalInjector implements Injector {
    @Override
    public void inject(List analyzers) {
        AnimalAnalyzer animalAnalyzer = null;
        for (Analyzer analyzer : analyzers) {
            if (analyzer instanceof AnimalAnalyzer) {
                animalAnalyzer = (AnimalAnalyzer) analyzer;
            }
        }
        if (animalAnalyzer == null) {
            throw new IllegalStateException("AnimalAnalyzer not found in list");
        }
        ... (bytecode generation and injection)
    }
}

One of the reasons that I chose to do it this way is that the analyzers have to be run in a specific order. For example, to identify a Dog which extends Animal, the latter has to be found first. For this purpose I created MasterAnalyzer, which stores a list of the analyzers in a specific order. When run is invoked on this analyzer, it calls run on the analyzers in the list in the right order.

public class MasterAnalyzer extends Analyzer {
    private List analyzers = new ArrayList<>();

    public MasterAnalyzer() {
        analyzers.add(new AnimalAnalyzer());
        analyzers.add(new DogAnalyzer());
        ... (more analyzers)
    }

    public List getAnalyzers() {
        return analyzers;
    }

    @Override
    public void run(ClassCollection classCollection) {
        for (Analyzer analyzer : analyzers) {
            analyzer.run(classCollection);
        }
    }
}

The injectors, in turn, can all be run with the same command: inject(masterAnalyzers.getAnalyzers()). While this makes it easy at the client side, it causes the injectors to know more than they need to know. For instance, let's assume that we also have a CatAnalyzer. Now, when we run the DogInjector, the list of analyzers will include the CatAnalyzer, even though the DogInjector does not care about it. This, along with the nasty instanceof usages, causes red signals to me. Now I am reaching out to you more experienced developers for some tips on how to improve my design. While searching for alternative ways, I found the Visitor pattern, which could be applicable here. However, I am still unsure how to apply it and whether it would improve my design or not.