EndOfAll
EndOfAll

Reputation: 365

Best way to store an ordered list of objects that are of different types in Java?

I have several classes that are used to create objects. These objects each have code that must be run that depends on the order of insertion.

They need to be stored in order - and recalled in the order they were stored.

  1. Object A
  2. Object B
  3. Object A
  4. Object A
  5. Object C
  6. Object D

I'm not sure if the proper way of handling this in Java is a generic list and checking typing at runtime.

List<Object> list = new ArrayList<Object>();

I've also thought about storing each object type in their own typed list with a parent object reading from the lists in the proper order. However, this seems more complex than just dealing with checking object types at runtime.

What is the proper "Java" way to handle this?

Upvotes: 2

Views: 945

Answers (1)

Marco R.
Marco R.

Reputation: 2720

If the objects do not share a common ancestor:

These classes aren't related. They share no common ancestor.

Then what you can do is to create another class that acts as a wrapper for:

  1. An object of type T.
  2. A Consumer<T> object to act as a reference to the code that needs to be invoked.

For example:

    class Invocable<T> {
        private final T target;
        private final Consumer<T> invocation;
        
        public Invocable(T target, Consumer<T> invocation) {
            this.target = target;
            this.invocation = invocation;
        }
        
        public void runInvocation() {
            invocation.accept(target);
        }
    }

Then create another class that manages a List<Invocable> like the following:

class RunnableList {
    
    private List<Invocable<?>> invocables = new ArrayList<Invocable<?>>();
    
    public <T> void add(T target, Consumer<T> invocation) {
        invocables.add(new Invocable<T>(target, invocation));
    }
    
    public void run() {
        invocables.forEach(Invocable::runInvocation);
    }
}   

And that's it! Just add ANY object to the RunnableList using the add(T target, Consumer<T> invocation) method and when you are done adding all your objects (with a reference to the respective code to be invoked) just invoke run on the RunnableList.

The following is a full working example of this, try it out to get the idea:

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

public class RunnableList {
    
    private List<Invocable<?>> invocables = new ArrayList<Invocable<?>>();
    
    public <T> void add(T target, Consumer<T> invocation) {
        invocables.add(new Invocable<T>(target, invocation));
    }
    
    public void run() {
        invocables.forEach(Invocable::runInvocation);
    }

    static class Invocable<T> {
        private final T target;
        private final Consumer<T> invocation;
        
        public Invocable(T target, Consumer<T> invocation) {
            this.target = target;
            this.invocation = invocation;
        }
        
        public void runInvocation() {
            invocation.accept(target);
        }
    }
    
    // TEST
    
    public static void main(String[] args) {
        RunnableList runnableList = new RunnableList();
        runnableList.add(new ClassA(), o -> o.run1("hello from A1"));
        runnableList.add(new ClassB(), o -> o.run1("hello from B1"));
        runnableList.add(new ClassC(), o -> o.run1("hello from C1"));

        runnableList.add(new ClassA(), ClassA::run2);
        runnableList.add(new ClassB(), ClassB::run2);
        runnableList.add(new ClassC(), ClassC::run2);
        runnableList.run();
    }
    
    static class ClassA {
        public void run1(String msg) {
            System.out.println("A.run1: " + msg);
        }
        public void run2() { System.out.println("A.run2"); }
    }
    static class ClassB {
        public void run1(String msg) {
            System.out.println("B.run1: " + msg);
        }
        public void run2() { System.out.println("B.run2"); }
    }
    static class ClassC {
        public void run1(String msg) {
            System.out.println("C.run1: " + msg);
        }
        public void run2() { System.out.println("C.run2"); }
    }
}

Complete code on GitHub

Hope this helps.

Upvotes: 3

Related Questions