This discussion is archived
12 Replies Latest reply: Sep 17, 2010 10:20 AM by 843793 RSS

Singleton of Generic custom class: Cannot make a static reference ...

416044 Newbie
Currently Being Moderated
I use singletons often for some static caches. Now I have a generic class "Cache<K, V>" that I want to subclass and use the singleton pattern:
public final class StaticCache<K, V> extends Cache<K, V> {
    static private StaticCache<K, V> instance = null;
}
But the declaration of the variable "instance" fails:

non-static class K cannot be referenced from a static context

non-static class V cannot be referenced from a static context

Of course, I don't want K,V static but only my instance. Is this possible at all?
  • 1. Re: Singleton of Generic custom class: Cannot make a static reference ...
    EJP Guru
    Currently Being Moderated
    A singleton of a generic class makes no sense to me. What about all the possible values of K times the possible values of V? How is one instance going to satisfy those?
  • 2. Re: Singleton of Generic custom class: Cannot make a static reference ...
    416044 Newbie
    Currently Being Moderated
    If it makes no sense to you, doesn't mean that it doesn't make sense to others. I want to call different singletons for e.g.

    myTextCache = StaticCache<String, String>.getInstance();

    but also

    myObjectCache = StaticCache<Date, Object>.getInstance();

    Is this (or similar) possible and ensuring a single instance with the singleton pattern?
  • 3. Re: Singleton of Generic custom class: Cannot make a static reference ...
    796085 Newbie
    Currently Being Moderated
    But since you've got a static 'instance', how are those two calls going to return different caches?
  • 4. Re: Singleton of Generic custom class: Cannot make a static reference ...
    416044 Newbie
    Currently Being Moderated
    dannyyates wrote:
    But since you've got a static 'instance', how are those two calls going to return different caches?
    That's right, but how can it be done (if at all)?
  • 5. Re: Singleton of Generic custom class: Cannot make a static reference ...
    EJP Guru
    Currently Being Moderated
    It can't. Because it doesn't make sense. There is only one instance. Java Generics aren't like C++ templates. Generics doesnt create new classes. There is only one class.
  • 6. Re: Singleton of Generic custom class: Cannot make a static reference ...
    416044 Newbie
    Currently Being Moderated
    I think it makes sense. I got it working now:
    public class StaticCache<K,V> extends Cache<K, V> {
    
        /** Singleton instance. */
        static private Map<Class<?>, Object> instances = new HashMap<Class<?>, Object>();
        
        
        /**
         * Get instance for a specific type.
         * 
         * @param <K> Type for key.
         * @param <V> Type for value.
         * @param type Requested type (e.g. {@literal getInstance()}).
         * @return Instance for the requested type.
         * @throws IllegalAccessException
         * @throws InstantiationException
         */
        static public synchronized <K,V> StaticCache<K,V> getInstance(Class<StaticCache<K,V>> type) throws IllegalAccessException, InstantiationException {
    
            StaticCache<K,V> result = null;
            
            if (type != null) {
                final Object o = instances.get(type);
                if (o != null && o instanceof StaticCache) { //instance already existing?
                    result = type.cast(o);
                } else { //create instance:
                    result = type.cast(type.newInstance());
                    if (result != null) {
                        instances.put(type, result);
                    }
                }
            }
            
            return result;
        }
    
        static public void main(String[] args) throws Exception {
            @SuppressWarnings("unchecked")
            final StaticCache<String, Date> so = StaticCache.getInstance((Class<StaticCache<String,Date>>) new StaticCache<String,Date>().getClass());
            
            @SuppressWarnings("unchecked")
            final StaticCache<Date, String> ds = StaticCache.getInstance((Class<StaticCache<Date, String>>) new StaticCache<Date, String>().getClass());
            
            final Date d1 = new Date();
            so.add("test1", d1);
            
            final Date d2 = new Date();
            ds.add(d2, "test2");
            
            final Date r1 = so.find("test1");
            final String r2 = ds.find(d2);
            
            
            System.out.println("Cache1: "+r1);
            System.out.println("Cache2: "+r2);
        }
    }//StaticCache
    The tricky part is the getClass() method that just returns Class<?> so I need to cast it.
  • 7. Re: Singleton of Generic custom class: Cannot make a static reference ...
    843793 Newbie
    Currently Being Moderated
        /** Singleton instance. */
        static private Map<Class<?>, Object> instances = new HashMap<Class<?>, Object>();
    Please learn what the "single" in "singleton" means. This is a multiton. And at a glance it looks pretty broken. This is a red flag:
    Class<StaticCache<K,V>>
    Since that will always be the same as
    StaticCache.class
    regardless of type parameters, due to erasure.

    What your code is really doing is just creating a single cache the first time you request any cache, and reassigning it different type parameters every subsequent time you request a cache of any parameters. So in implementation it's absolutely no different than just storing some raw singleton and just doing an unchecked cast to your type parameters:
    private static final StaticCache instance = new StaticCache();
    static public synchronized <K,V> StaticCache<K,V> getInstance(Class<StaticCache<K,V>> type) {
        return StaticCache<K, V> instance;
    }
    Not that I'm advocating that. It might work as long as you stick to StaticCache and not some subclass that reifies the parameters.

    It seems you're doing all this to trick clients of your library into thinking it's type safe. If you actually need separate instances, you should be accepting the class objects for K and V and using those as a composite key in your map of caches (optionally along with a String to semantically differentiate between caches that have the same key/value types but are used for different things).

    All of this smells to high heaven though.

    Edited by: endasil on 16-Sep-2010 2:00 PM
  • 9. Re: Singleton of Generic custom class: Cannot make a static reference ...
    416044 Newbie
    Currently Being Moderated
    Please learn what the "single" in "singleton" means. This is a multiton.
    Just forgot to change the javadoc after my implementation change.
    What your code is really doing is just creating a single cache the first time you request any cache, and reassigning it different type parameters every subsequent time you request a cache of any parameters. So in implementation it's absolutely no different than just storing some raw singleton and just doing an unchecked cast to your type parameters:
    Seems so. But the calling method doesn't know anything about the casts. For him, this is transparent and he uses the class API as expected.
    It seems you're doing all this to trick clients of your library into thinking it's type safe. If you actually need separate instances, you should be accepting the class objects for K and V and using those as a composite key in your map of caches (optionally along with a String to semantically differentiate between caches that have the same key/value types but are used for different things).

    All of this smells to high heaven though.
    Because what you suggest is not what I want.

    I know that StaticCache<K,V> can not have a "singleton" (in the classic sense), but can have singletons for each different type of StaticCache<K,V>. Of course, I could omit the generics and just store <Object, Object> and use it for any values I want to cache. But my requirement is to have different kinds of caches (each containing specific types and can have different refresh/clean cycles). I could use <Object,Object> but then the calling method has to cast the specific types and as we all know, casting should be avoided as much as possible. So I try to minimize the casts and still try to use generics to avoid redundant code. My problem is the "static" nature of misc caches. So far I have cache instances for each application module. To make them static, each module has its own static cache. But now I want to access all those static caches at once (from a client app) and thought about interfaces or a superclass. As my caches are all subclasses of Cach<K,V>, I thought about introducing a subclass StaticCache<K,V> extending Cache<K,V> that handles the static nature (or "singleton") transparently. So, the generic super class Cach<K,V> has to stay and therefore, the subclass StaticCahce also needs the <K, V> types. Perhaps, I should think about a complete different approach, but subclassing should be a natural solution for my requirement (?).

    Here's my new approach:
    public class StaticCache<K,V> extends Cache<K, V> {
    
        /** Instances (singletons) for each name. */
        static private Map<String, StaticCache<?,?>> instances = new HashMap<String, StaticCache<?,?>>();
        
        /**
         * Constructor.
         */
        public StaticCache() {
            final WeakHashMap<K, V> m = new WeakHashMap<K, V>();
            data = Collections.synchronizedMap(m);
        }//Cache()
        
        /**
         * Get instance for a specific type.
         * 
         * @param typeKey Type for key.
         * @param typeValue Type for value.
         * @return Instance for the requested type.
         */
        @SuppressWarnings({ "unchecked", "rawtypes" })
        static public synchronized <K,V> StaticCache<K,V> getInstance(final String name, final Class<K> typeKey, final Class<V> typeValue) {
    
            StaticCache<K,V> result = null;
            
            if (name != null && typeKey != null && typeValue != null) {
                final StaticCache<?,?> sc = instances.get(name);
                if (sc != null) {
                    result = (StaticCache) sc; //dangerous: caller can set different K,V types for existing cache!
                } else {
                    result = new StaticCache<K,V>();
                    instances.put(name, result);
                }
            }
            
            return result;
        }//getInstance()
    
        
        static public void main(String[] args) throws Exception {
            final StaticCache<String, Date> so = StaticCache.getInstance("Cache1", String.class, Date.class);
            final StaticCache<Date, String> ds = StaticCache.getInstance("Cache2", Date.class, String.class);
            
            final Date d1 = new Date();
            so.add("test1", d1);
            
            final Date d2 = new Date();
            ds.add(d2, "test2");
            
            final Date r1 = so.find("test1");
            final String r2 = ds.find(d2);
            
            
            System.out.println("Cache1: "+r1);
            System.out.println("Cache2: "+r2);
            
            new java.util.Locale("de");
        }
    }//StaticCache
  • 10. Re: Singleton of Generic custom class: Cannot make a static reference ...
    800025 Explorer
    Currently Being Moderated
    I followed this thread for awhile, but I'm still not quite confident that I understand what you actually want to achieve. Therefore just a sample of an implementation that you might be looking for.
    import java.util.*;
    
    public class StaticCache<K, V> extends Cache<K, V> {
    
        private static class Combination {
         final Class<?> key;
         final Class<?> value;
    
         public Combination(Class<?> key, Class<?> value) {
             this.key = key;
             this.value = value;
         }
    
         @Override
         public boolean equals(Object other) {
             if (other == null || !(other instanceof Combination)) {
              return false;
             }
             return this.key.equals(((Combination) other).key)
                  && this.value.equals(((Combination) other).value);
         }
    
         @Override
         public int hashCode() {
             return key.hashCode() ^ value.hashCode();
         }
        }
    
        static private Map<Combination, Object> instances = new HashMap<Combination, Object>();
    
        @SuppressWarnings("unchecked")
        static public synchronized <K, V> StaticCache<K, V> getInstance(
             Class<K> keyClass, Class<V> valueClass) {
         Combination combi = new Combination(keyClass, valueClass);
         StaticCache<K, V> rv = (StaticCache<K, V>) instances.get(combi);
         if (rv == null) {
             rv = (StaticCache<K, V>) new StaticCache();
             instances.put(combi, rv);
         }
         return rv;
    
        }
    
        static public void main(String[] args) throws Exception {
    
         final StaticCache<String, Date> so = StaticCache.getInstance(
              String.class, Date.class);
         System.out.println("Same types give same cache: "
              + (so == StaticCache.getInstance(String.class, Date.class)));
    
         final StaticCache<Date, String> ds = StaticCache.getInstance(
              Date.class, String.class);
         System.out.println("Different types give same cache: "
              + (((Object) so) == ds)); // <== see below
    
         // boolean test = so == ds; // Compare objects with different types
    
         final Date d1 = new Date();
         so.add("test1", d1);
    
         final Date d2 = new Date();
         ds.add(d2, "test2");
    
         final Date r1 = so.find("test1");
         final String r2 = ds.find(d2);
    
         System.out.println("Cache1: " + r1);
         System.out.println("Cache2: " + r2);
        }
    
        private StaticCache() {
    
        }
    }
    
    class Cache<K, V> {
    
        @SuppressWarnings("unchecked")
        private Map<K, V> data = new HashMap();
    
        public void add(K key, V value) {
         data.put(key, value);
        }
    
        public V find(K key) {
         return data.get(key);
        }
    
    }
  • 11. Re: Singleton of Generic custom class: Cannot make a static reference ...
    416044 Newbie
    Currently Being Moderated
    @pietblock: I think this is quite the same solution as mine - I just followed the idea of endasil and use explizit cache names instead of just different caches for different types. This also resolves the problem of returning the same instance. As there might be some caches with the same types but different usages. Both our codes have the weakness of casting from StaticCache to StaticCache<K,V>. I was able to do this (same cache name with different types):
    public class StaticCache<K,V> extends Cache<K, V> {
    
        /** Instances (singletons) for each name. */
        static private Map<String, StaticCache<?,?>> instances = new HashMap<String, StaticCache<?,?>>();
        
        /**
         * Constructor.
         */
        public StaticCache() {
            final WeakHashMap<K, V> m = new WeakHashMap<K, V>();
            data = Collections.synchronizedMap(m);
        }//Cache()
        
        /**
         * Get instance for a specific type.
         * 
         * @param id Unique ID for cache.
         * @param typeKey Type for key.
         * @param typeValue Type for value.
         * @return Instance for the requested type. If the instance was already created before, the result key/value types are those of the
         * first instantiation (i.e. the <i>typeKey</i>, <i>typeValue</i> are ignored).
         */
        @SuppressWarnings({ "unchecked"})
        static public synchronized <K,V> StaticCache<K,V> getInstance(final String id, final Class<K> typeKey, final Class<V> typeValue) {
    
            StaticCache<K,V> result = null;
            
            if (id != null && typeKey != null && typeValue != null) {
                final StaticCache<?,?> sc = instances.get(id);
                if (sc != null) {
                    result = (StaticCache<K,V>) sc; //dangerous: caller can set different K,V types for existing cache!
                } else {
                    result = new StaticCache<K,V>();
                    instances.put(id, result);
                }
            }
            
            return result;
        }//getInstance()
    
        
        static public void main(String[] args) throws Exception {
            final StaticCache<String, Date> c1 = StaticCache.getInstance("Cache1", String.class, Date.class);
            final StaticCache<Date, String> c2 = StaticCache.getInstance("Cache1", Date.class, String.class);
            
            final Date d1 = new Date();
            c1.add("test1", d1);
            
            final Date d2 = new Date();
            c2.add(d2, "test2");
            
            final Date r1 = c1.find("test1");
            final String r2 = c2.find(d2);
            
            
            System.out.println("Cache1: "+r1);
            System.out.println("Cache2: "+r2);
            
            new java.util.Locale("de");
        }
    }//StaticCache
    without compiler or runtime error. So endasil's comment "It seems you're doing all this to trick clients of your library into thinking it's type safe." is firing here ... :-(

    The declaration of instance should be
        /** Instances (singletons) for each name. */
        static private Map<String, StaticCache<K,V>> instances = new HashMap<String, StaticCache<K,V>>();
    but this doesn't compile: Cannot make a static reference to the non-static type K
  • 12. Re: Singleton of Generic custom class: Cannot make a static reference ...
    843793 Newbie
    Currently Being Moderated
    This is close to what I was suggesting, but not quite.
    result = (StaticCache<K,V>) sc; //dangerous: caller can set different K,V types for existing cache!
    This is still type unsafe as you note because sc does not take into account whether the method was called with the right type parameters. What I would suggest is integrating the class objects for K/V into the key of the map.
    public final class CacheKey<K, V> {
         private final String name;
         private final Class<?> k, v;
         //constructor, equals, and hashcode implemented properly
    }
    
    static private Map<CacheKey<?,?>, StaticCache<?,?>> instances;
    
    @SuppressWarnings({ "unchecked"})
    static public synchronized <K,V> StaticCache<K,V> getInstance(CacheKey<K, V> key) {
         return StaticCache<K, V> instances.get(key); //or create, etc
    }
    Then clients, instead of storing the key as a constant String, would store the composite key:
    public static final CacheKey<String, String> propertyCacheKey = new CacheKey<String, String>("properties", String.class, String.class);
    As a result, if clients called getInstance with the wrong classes, instead of getting a type-unsafe version of the static cache, they would get back null or a new cache would be created (you decide). The primary point is type safety.