8

So I have a few 'Manager' classes, for example GroupManager. All these Managers are singletons.

Using this method for instancing:

private static GroupManager groupManager = null;

private GroupManager()
{

}

public static GroupManager Instance()
{
    if (groupManager == null)
    {
        groupManager = new GroupManager();
    }
    return groupManager;
}

I'm thinking I should start to use some inheritance as they have a lot of copied methods.

The Instance() methods for each Manager is the same.

So for inheritance i can do this (obviously):

GroupManager extends Manager

Is it possible to use generics to use the same Instance method for all managers, something like:

public class Manager<E>
{
    private static E instance = null;

    public static E Instance()
    {
        if (instance == null)
        {
            instance = new E();
        }
        return instance;
    }

}

I think that makes sense :)

So then you would do GroupManager.Instance() like normal.

Dave Newton
  • 158,873
  • 26
  • 254
  • 302
Metalstorm
  • 2,940
  • 3
  • 26
  • 22
  • 1
    Java Generics are in no way the same as C++ Templates! Let it go! You can never allocate an object (i.e. call new) using a Java Generic. Try a google search for "Java Type Erasure" – DwB Nov 02 '11 at 14:40
  • Related: http://stackoverflow.com/questions/1927789/why-should-i-care-that-java-doesnt-have-reified-generics – BalusC Nov 02 '11 at 14:43
  • Manager E is a reference to a generic type. There is no simple way to setup a class to allocate objects of an type that is only known at run time. Reid Mac's answer is what I would try. – DwB Nov 02 '11 at 14:54
  • The limitations you're running into on hand-coded singletons are a big part of why people use containers like Spring or Guice to manage their singletons' lifecycle. – Nathan Hughes Nov 02 '11 at 14:55
  • Additionally, tihs won't compile: "Cannot make a static reference to the non-static type E" – zeller Nov 02 '11 at 14:55

5 Answers5

10

You don't understand how generics and statics work. If you have a static field or method (such as "instance" or instance()), which can be called without instantiating the class Manager, how do you expect the JVM (and the compiler even) to know what type E is supposed to be?

Here's an example, as per G_H's suggestion:

GeneralManager and AreaManager both extend Manager

The Manager class is the only one that has the getInstance() static method:

    public class Manager {

        private static Map<Class<? extends Manager>,Manager> INSTANCES_MAP = new java.util.HashMap<Class<? extends Manager>, Manager>();

//Also, you will want to make this method synchronized if your application is multithreaded,
//otherwise you mihgt have a race condition in which multiple threads will trick it into
//creating multiple instances
        public static <E extends Manager> E getInstance(Class<E> instanceClass) throws InstantiationException, IllegalAccessException {
            if(INSTANCES_MAP.containsKey(instanceClass)) {
                return (E) INSTANCES_MAP.get(instanceClass);
            } else {
                E instance = instanceClass.newInstance();
                INSTANCES_MAP.put(instanceClass, instance);
                return instance;
            }
        }
    }
Shivan Dragon
  • 15,004
  • 9
  • 62
  • 103
  • 1
    Yeah, but that's totally not gonna work for the idea of implementing singletons. You'd need some horribly contorted structure like a Map from `Class`es to instances. – G_H Nov 02 '11 at 14:47
  • +1, that's very true, I was thinking if I should add that observation as well or not (I decided not to cause I believe first the basic notions of generics and statics need to be understood before being used like this). However you make a good point. Further more, you could restrict the generic E type to a base class like E extends RootOfIheritanceChainClass. – Shivan Dragon Nov 02 '11 at 14:51
  • I'm kinda lost lol, should i just go with: basically i need a new Instance() method for each new Manager. – Metalstorm Nov 02 '11 at 14:54
  • If you have multiple classes iheriting from Manager, and you want to only have one instance of each of these classes, then you should declare static getInstance() methods in each one that only return the one instance of the thing. The static methods are not overloaded so they'll all work independently. That's the simplest way to do it, you get a bit of duplicated code, but not much. Or you can just make one static getInstance() method in the base class, and keep a Class/ClassInstance map of all singletons. – Shivan Dragon Nov 02 '11 at 14:59
  • "Or you can just make one static getInstance() method in the base class, and keep a Class/ClassInstance map of all singletons." could you explain that a bit further please, and if possible an example. – Metalstorm Nov 02 '11 at 15:03
  • Y'know, these days enums are commonly used as a convenient implementation of singletons. Maybe something can be done with an enum with multiple constants... one for each subtype. I'm gonna try that. – G_H Nov 02 '11 at 16:07
2

Nope, it's not gonna work. Java uses generics at compile time for type checking, but doesn't generate extra classes or retain info regarding type parameters at runtime.

When you declare Manager<E> with that type parameter E, that's something that will only play a role in an actual instance. You could have a subclass like GroupManager extends Manager<String> or whatever, but that's not magically gonna generate a variety of the static method.

Static methods and members belong with a class, not an instance. So trying to use generics there, which are intended for typing instances, isn't gonna fly.

G_H
  • 11,739
  • 3
  • 38
  • 82
0

public class Manager<E>{

private static Object instance = null;

    public static E Instance() {
       if (instance == null)
       {
        instance = new E();
       }
       return (E)instance;
   }
} 

Lohith Ravi
  • 68
  • 2
  • 6
0

If you make your group manager class as follows then you can call your instance method.

public class GroupManager extends Manager<GroupManager>{}

And in your Manager class try this...

public class Manager<E>
{
private static E instance = null;

public static E Instance()
{
                  try {
            return instance.newInstance();
        } catch (InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return null;  
}

Or if you know the object you want an instance for, just make the method generic

public static <T> T getInstance(Class<T> t){
             try {
            return t.newInstance();
        } catch (InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return null;
    }

I havn't tried any of this so not sure if it will work.

Reid Mac
  • 2,411
  • 6
  • 37
  • 64
0

Injecting the constructor in a generic context. Cash is not thread safe, but is only used in static context so its fine if you don't miss use it

public class Example {

    public static class MySingletonClass {
    }

    public interface Provider<T> {
        T get();
    }

    static final Provider<MySingletonClass> myClassInstanceProvider = new Cash<MySingletonClass>(new Provider<MySingletonClass>() {
            @Override
            public MySingletonClass get() {
                return new MySingletonClass();
            }
        }); 


    public static class Cash<T> implements Provider<T> {
        private Provider<T> provider;

        public Cash(Provider<T> provider) {
            this.provider = provider;
        }

        @Override
        public T get() {

            final T t = provider.get();
            provider = new Provider<T>() {

                @Override
                public T get() {
                    return t;
                }
            };
            return t;
        }
    }
}
user976283
  • 54
  • 3