12 Replies Latest reply: Sep 17, 2010 12:20 PM by 843793 RSS

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

    416044
      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
          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
            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
              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
                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
                  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
                    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
                          /** 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
                        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
                          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
                            @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
                              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.