Generic ObjectPool - how to return a generic class?
Asked Answered
M

3

7

I am trying to develop an ObjectPool, which can be used with any Object without changing the source of neither the Pool nor the Object - but I can´t find any way to write the get()-function ("Maincode" getting some Object from Pool) as there is a Type-Mismatch (Cannot convert from Object to TestObject)

Here´s my code so far:

ObjectPool:

public Object get() {
        int first = availableObjects.get(0);
        availableObjects.remove(0);
        return objects.get(first);
    }

In objects (ArrayList) there are all the objects in the pool, availableObjects just contains a list of all available objects. I am basically just returning the first available object and mark it as unavailable.

Maincode:

    TestObject test = objectPoolS.get();


How can I make the get()-Method more generic/specific so it works without adapting the pool everytime I use a different class and without parsing on the maincode?


Update:

With a more generic version of that Pool, how would the get()-method look like correctly?
(Any other feedback greatly appreciated!)
Update: Working (Fixed) Version of the MainCode below

Update 2:
I just realized that it can´t work like this, I need to create new Objects in the Pool - which seems hardly possible to do without adapting the Pool for every kind of Object.
So can I somehow remove the reference between all the Objects in the Pool? (As I use baseObject to initialize all Objects, I actually only have 5 references to 1 Object)
Or is there a better way to manage it? (Without forcing the user to create the Objects in his Code)

Complete Code so far:

ObjectPool

public class ObjectPoolS<T> {

    int numberOfObjects;
    boolean autoExtending;

    T baseObject;
    ArrayList<T> objects;
    ArrayList<Integer> availableObjects;


    public  ObjectPoolS(T baseObject, int capacity, boolean allowAutoExtending) {
        this.numberOfObjects = capacity;
        this.baseObject = baseObject;
        this.autoExtending = allowAutoExtending;

        objects = new ArrayList<T>(capacity);
        availableObjects = new ArrayList<Integer>(capacity);

        initialize(baseObject, capacity);
    }


    private void initialize(T baseObject, int capacity) {

        // Initialize List of Objects
        for(int i = 0; i < capacity; i++) {
            objects.add(baseObject);
        }

        // Initialize Index of Objects
        for(int i = 0; i < capacity; i++) {
            availableObjects.add(new Integer(i));
        }
    }


    public T get() {
        int first = availableObjects.get(0);
        availableObjects.remove(0);
        return objects.get(first);
    }
}

Maincode (Original)

ObjectPoolS objectPoolS = new ObjectPoolS(new TestObject(0), 5, true);
TestObject test = objectPoolS.get();

Maincode (Fixed)

ObjectPoolS<TestObject> objectPoolS = new ObjectPoolS<TestObject>(new TestObject(0), 5, true);
TestObject test = objectPoolS.get();

TestObject only contains a single int for testing purposes

Error

Type mismatch: cannot convert from Object to TestObject
Manvil answered 27/12, 2013 at 17:27 Comment(7)
i see a threading issue here if this is going to be called by multiple requests.Diarmuid
so i´d better use it with synchronized?Manvil
yes, you will need to sync that piece... you NEVER want to allow more than 1 thread add/remove from a list...Diarmuid
Are there any remaining issues? After your update it looks like you're on the right track, assuming there's a type parameter T being declared at the class level.Jenninejennings
The Maincode still has a Type-Mismatch which I cannot resolveManvil
Are you declaring your class as e.g. class ObjectPool<T> ? Please add an SSCCE and the compiler error to your question. I also recommend unaccepting the existing answer because it's not a safe solution.Jenninejennings
@PaulBellora updated my question, should contain anything needed - I will unaccept the answer as soon as there is a better one.Manvil
B
3

You are not pooling: you are using just single instance of the object.

Generic object pool looks something like this:

public class ObjectPool<T> {
    private List<T> objects;
    private Class<T> clazz;
    private int size;

    public ObjectPool(Class<T> clazz, int size) throws IllegalStateException {
        this.clazz = clazz;
        this.size = size;
        this.objects = new ArrayList<T>();
        for (int i = 0;i < size;i++) {
            objects.add(newInstance());
        }
    }

    // You can override this method with anonymous class
    protected T newInstance() {
        try {
            return clazz.newInstance()
        } catch (Exception exception) {
            throw new IllegalStateException(exception);
        }
    }

    public synchronized T getInstance() {
        if (objects.isEmpty()) {
            return null;
        }
        return objects.remove(objects.size() - 1);
    }

    public synchronized void returnInstance(T instance) {
        if (objects.size() < size) {
            objects.add(instance);
        }
    }
}

Unfortunately I do not have compiler to test the code, but it should give you a hint what to change.

Brawn answered 28/12, 2013 at 18:10 Comment(2)
That´s pretty much exactly what I was looking for (update2) - only problem so far is a compile error: "Unhandled exception type IllegalAccessException" when adding a newInstance to objects - Btw, shouldn´t I "reset"/reinitialize an object while returning it to the pool?Manvil
newInstance() requires public default constructor. I'll alter the code a bit, so you can created instanes of classes that do not have default constructor by overriding a methodBrawn
D
3

Oh, in your main code

ObjectPoolS objectPoolS = new ObjectPoolS(new TestObject(0), 5, true);

you must add "<TestObject>" and becomes

ObjectPoolS<TestObject> objectPoolS
=new ObjectPoolS<TestObject> (new TestObject(0),5,true);

just like where you declare ArrayList, it contains "<T>" and "<Integer>", isn't it?

However, I believe that there should be no problem in your original code. It maybe another problem.

Tips: at the get() method, you can rewrite to:

public T get(){
    return this.list.get(this.available.remove(0));
}
Dunaville answered 28/12, 2013 at 17:28 Comment(1)
Forgot about that - works now! Thanks for the tip, didn´t realize that .remove() actually returns somethingManvil
B
3

You are not pooling: you are using just single instance of the object.

Generic object pool looks something like this:

public class ObjectPool<T> {
    private List<T> objects;
    private Class<T> clazz;
    private int size;

    public ObjectPool(Class<T> clazz, int size) throws IllegalStateException {
        this.clazz = clazz;
        this.size = size;
        this.objects = new ArrayList<T>();
        for (int i = 0;i < size;i++) {
            objects.add(newInstance());
        }
    }

    // You can override this method with anonymous class
    protected T newInstance() {
        try {
            return clazz.newInstance()
        } catch (Exception exception) {
            throw new IllegalStateException(exception);
        }
    }

    public synchronized T getInstance() {
        if (objects.isEmpty()) {
            return null;
        }
        return objects.remove(objects.size() - 1);
    }

    public synchronized void returnInstance(T instance) {
        if (objects.size() < size) {
            objects.add(instance);
        }
    }
}

Unfortunately I do not have compiler to test the code, but it should give you a hint what to change.

Brawn answered 28/12, 2013 at 18:10 Comment(2)
That´s pretty much exactly what I was looking for (update2) - only problem so far is a compile error: "Unhandled exception type IllegalAccessException" when adding a newInstance to objects - Btw, shouldn´t I "reset"/reinitialize an object while returning it to the pool?Manvil
newInstance() requires public default constructor. I'll alter the code a bit, so you can created instanes of classes that do not have default constructor by overriding a methodBrawn
U
-2

You can try :

public <T> T get() {
    // Your code for objectToReturn
    return (T) objectToReturn;
}
Underlet answered 27/12, 2013 at 17:32 Comment(8)
Wouldn't you use a generic ArrayList (or Queue)?Crabbe
@BheshGurung in what situation would that happen?Manvil
One of the purpose of generics is the type-safety, you don't want a explicit cast like that. The actual type is inferred in this case, and if you use a wrong type to create a reference then you get a CCE.Backstitch
What would you recommend instead?Manvil
Make the class itself generic, assuming all the objects in the pool are of the same type.Backstitch
@BheshGurung doesn´t quite work yet - please take a look at my updated questionManvil
-1 This is worse than forcing the caller to cast, because there's no visible sign at the call site that something can go wrong. With this code, the caller could ask for a List<String> and be given a List<Integer> instead, then run into an inexplicable ClassCastException later on.Jenninejennings
There should be no way of returning a different Class/Object than provided/expected in my code - but generally I agree.Manvil

© 2022 - 2024 — McMap. All rights reserved.