3

I am trying to make a singleton like the following, but I keep getting a warning. If possible, I don't want suppress warning. Is there a way to do it?

For now, I don't want to think about the thread-safety. I just want to pass this warning.

    public interface Storage<K, V> {
        public void put(K key, V value);
        public V get(K key);
    }


    public static class DefaultStorage<K, V> implements Storage<K, V> {

        private Map<Object, Object> map = new ConcurrentHashMap<Object, Object>();

        private static DefaultStorage<K, V> defaultStorage;

        private DefaultStorage() {
            //
        }

        public static DefaultStorage<?, ?> getInstance() {
            if (defaultStorage== null) {
                defaultStorage= new DefaultStorage();
            }
            return defaultStorage;
        }
     }

Thanks.

user826323
  • 2,248
  • 6
  • 43
  • 70

3 Answers3

2

The variable defaultStorage in DefaultStorage only exists once in every instance of a DefaultStorage combined. At runtime there's only one actual class, and one static variable. So, the variable will simultaneously be a DefaultStorage<K1, V1>, a DefaultStorage<K2, V2>, a DefaultStorage<K3, V3>, and so on. So, one class will store Strings in it, another will store BigDecimals, and another X501Principals. This subverts type-safety.

The warning is that you are storing an instance of a raw type, new DefaultStorage(), in a variable declared as DefaultStorage<K, V>.

From Angelika Langer's Generics FAQ,

Can generic types have static members?

Yes.

Generic types can have static members, including static fields, static methods and static nested types. Each of these static members exists once per enclosing type, that is, independently of the number of objects of the enclosing type and regardless of the number of instantiations of the generic type that may be used somewhere in the program. The name of the static member consists - as is usual for static members - of the scope (packages and enclosing type) and the member's name. If the enclosing type is generic, then the type in the scope qualification must be the raw type, not a parameterized type.

Community
  • 1
  • 1
Eric Jablow
  • 7,874
  • 2
  • 22
  • 29
  • That would mean if you created a `DefaultStorage d1` and a `DefaultStorage d2`, you could do `d2.put("X", BigDecimal.TEN)` and `d1.get("X")`—ooops, it's getting warm in here! I guess the Warning means: you don't want to do this. – Andrew Lazarus Jun 07 '13 at 04:51
  • Yes, this is also why you'd get a warning about accessing a static variable from an object instance. The compiler would suggest you use `DefaultStorage.defaultStorage`, or it might go right to suggesting `DefaultStorage.defaultStorage`. In either case, a `ClassCastException` will occur soon. This assumes that your `get` and `set` methods go through the static `defaultStorage`, of course. – Eric Jablow Jun 07 '13 at 04:56
1

I would use an enum and specify the type.

public interface Storage<K, V> {
    public void put(K key, V value);
    public V get(K key);
}

public enum DefaultStorage implements Storage<String, Object> {
    INSTANCE;

    private final Map<Object, Object> map = new ConcurrentHashMap<>();

    public Object get(String key) { return map.get(key); }
    public void put(String key, Object value) { map.put(key, value); }

}

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
0

How were you able to create a reference to your parameterized inner class like this?

private static DefaultStorage<K, V> defaultStorage;

As per the explanation given in Static method in a generic class?:

A class's type parameters is only available for its instance variables and not static fields and methods. Static fields and methods are shared among all instances of a class and even to instances of various type parameters."

So you cannot create a static reference to your class DefaultStorage and limit it to just K, V types. This should be fine.

private static DefaultStorage<?,?> defaultStorage;

But as you have mentioned I am not able to get rid of the warning without suppressing it. And creating the object like this

 defaultStorage= new DefaultStorage(); 
throws a compilation error obviously.
Community
  • 1
  • 1
veebee
  • 61
  • 10