1

I'm getting the error "Cannot create a generic array" for the following code:

public class MapImpl<K, V> {
    private static int DEFAULT_CAPACITY = 16;

    private int size;
    // array holding the entries of the map
    private Entry[] entries;

    public MapImpl() {
        entries = new Entry[DEFAULT_CAPACITY]; // error at this line: Cannot create a generic array of MapImpl<K,V>.Entry
    }

    // represents an entry in the map
    private class Entry {
        private K key;
        private V value;

        public Entry(K key, V value) {
            this.key = key;
            this.value = value;
        }
    }

Surprisingly, this works fine:

public class MapImpl<K, V> {
    private static int DEFAULT_CAPACITY = 16;

    private int size;
    // array holding the entries of the map
    private Entry<K, V>[] entries;

    public MapImpl() {
        entries = new Entry[DEFAULT_CAPACITY];
    }

    // represents an entry in the map
    private class Entry<K, V> {
        private K key;
        private V value;
//but here K and V are being hidden.
        public Entry(K key, V value) {
            this.key = key;
            this.value = value;
        }
    }

    }

I do understand that we can't create an array of generic type or a type that takes type parameters. But, here in my code the Entry class is not of a generic type. Am i overlooking something ?

geekprogrammer
  • 836
  • 1
  • 8
  • 37

5 Answers5

4

Problem here is that non-static nested class has access to all members of its outer classes, which includes information about generic types used in outer class, like

class Outer<T>{
    private T t;
    class Inner{
        void method(T tt){//we can use same type T as used in outer class
            t = tt;
        }
    }
}

So in reality Inner class type is more like Outer<T>.Inner which makes form of it generic type and arrays can't be created from generic types because of type erasure which would prevent arrays from being able to test if added elements are valid.

Most common solution in that cases is to use collections instead of arrays like List<OurType>.

But if you really want to have only arrays then other possible solution (but you should try to avoid it) is to use raw type, so instead of

new Entry[DEFAULT_CAPACITY];

which is equivalent of

new MapImpl<K, V>.Entry[DEFAULT_CAPACITY];

you could use

new MapImpl.Entry[DEFAULT_CAPACITY];
//         ^no generic type -> it is raw type

Solution with

private class Entry<K, V> {
    private K key;
    private V value;
//but here K and V are being hidden.
    public Entry(K key, V value) {
        this.key = key;
        this.value = value;
    }
}

works probably (I can't find any relevant JLS describing this) because as you said, you have shadowed original K and V from outer class, which means you can't access them and now

        void method(T tt){
            t = tt;
        }

method will not compile because T from inner class is not the same as T from outer class. Because of that, Entry is no longer MapImpl<K,V>.Entry but MapImpl.Entry<K,V> and when you write it as

new Entry[...]

you are explicitly making it raw type which will work (with compilation warning about rawtypes when you declare private Entry[] entries)

Pshemo
  • 113,402
  • 22
  • 170
  • 242
2

Declare Entry class as static. Currently it's not static, so it's implicitly linked to the MapImpl instance and to its generic arguments.

Edit: I mean

 private static class Entry<K, V>
Tagir Valeev
  • 87,515
  • 18
  • 194
  • 305
2

Since Entry is an inner class of a generic class MapImpl, it is also parametrized by K and V. To make an array, you will have to create it with raw type:

entries = new MapImpl.Entry[DEFAULT_CAPACITY];
Misha
  • 25,007
  • 4
  • 52
  • 71
  • thanks for the answer. May I know the difference between new Entry[DEFAULT_CAPACITY] and new MapImpl.Entry[DEFAULT_CAPACITY] ? – geekprogrammer May 04 '15 at 11:59
  • As I wrote in the answer, by declaring `Entry` as an inner class of `MapImpl`, you are tying `Entry` to an instance of `MapImpl`. That makes `Entry` generic by `MapImpl`'s parametric types. The full name of class `Entry` is `MapImpl.Entry`. By creating array of `MapImpl.Entry`, you make the type raw rather then generic. – Misha May 04 '15 at 12:25
0

It's because Java's arrays (unlike generics) contain, at runtime, information about its component type. So you must know the component type when you create the array. Since you don't know what K and V are at runtime, you can't create the array.

Jordi Castilla
  • 24,953
  • 6
  • 58
  • 97
0

Try below code instead

package com.vibhs.stack.overflow.generics;

public class MapImpl<K, V> {
private static int DEFAULT_CAPACITY = 16;

private int size;
// array holding the entries of the map
private Entry[] entries;

public MapImpl() {
    entries = new Entry[DEFAULT_CAPACITY]; // error at this line: Cannot
                                            // create a generic array of
                                            // MapImpl<K,V>.Entry
}

// represents an entry in the map
private class Entry<K,V> {
    private K key;
    private V value;

    public Entry(K key, V value) {
        this.key = key;
        this.value = value;
    }
}
}
Shineed Basheer
  • 502
  • 4
  • 14