5

I have a sample code for tries.The code seems have no compile errors. Why does it use static nested class node? when I delete static in Node nested class and compile, the error shows create generic array in private Node[] next = new Node[R];. What on earth happened?

public class TrieST<Value> {
    private static final int R = 256;        // extended ASCII


    private Node root;      // root of trie
    private int N;          // number of keys in trie

    // R-way trie node
    private static class Node {
        private Object val;
        private Node[] next = new Node[R];
    }


    public TrieST() {
    }
}
user207421
  • 289,834
  • 37
  • 266
  • 440
Peterxwl
  • 763
  • 2
  • 11
  • 21
  • 4
    First, do you know why generic arrays aren't allowed? (e.g. `new ArrayList[5];` shows this error) – user253751 Feb 12 '15 at 01:58
  • possible duplicate of [How to create a generic array?](http://stackoverflow.com/questions/18581002/how-to-create-a-generic-array) – Nir Alfasi Feb 12 '15 at 02:15
  • Can you explain why there is no such error when static is added? – Peterxwl Feb 12 '15 at 02:18
  • Because with static you create: `Node[] next = new Node[R]` and with non-static inner-class you create a Node that is associated with an instance of the outer-class, which has a generic type. And creation of generic arrays is forbidden. – Nir Alfasi Feb 12 '15 at 03:32
  • 1
    Note that 'static inner' is a contradiction in terms. A nested class is *either* static *or* inner. – user207421 Feb 12 '15 at 03:34
  • @EJP good catch, non-static was supposed to be in brackets. – Nir Alfasi Feb 12 '15 at 03:41
  • See also ["Java Generics: Cannot create an array of a nested class"](http://stackoverflow.com/q/26926920/2891664). – Radiodef Feb 12 '15 at 08:24

2 Answers2

2

Assuming that in your code snippet you are using a non-static inner class instead of a static nested class like this: private class Node, in that case, you will be trying to instantiate an Array which is not possible, we can't instantiate an Array in a generic class, because generics doesn't have any information regarding their type at runtime, while arrays creation expression specifies the element type.

So, the reason why using a Static Nested Class compiled, is that such classes are considered as a "top-level" class (in terms of behavior):

A static nested class interacts with the instance members of its outer class (and other classes) just like any other top-level class. In effect, a static nested class is behaviorally a top-level class that has been nested in another top-level class for packaging convenience.

Now, let's take all of this into consideration, and come back to the exact error displayed by the compiler:

Cannot create a generic array of TrieST<Value>.Node

That means that the type of the array you want to create is TrieST<Value>.Node whose runtime's type is not known, thus different types may be inserted into the next array. More clear and well explained examples could be found in Cannot Create Arrays of Parameterized Types

Whereas, a static Nested class is not behaving as an inner class of TrieST<Value> , thus instiating an array inside Node will not be illegal as it's not of the type TrieST<Value>.Node, its of the type Node (like if it's a top-level class) .

Tarik
  • 4,546
  • 3
  • 33
  • 64
  • Can you explain more why and how nested static lass "suppress" this error consider as top level class? – Peterxwl Feb 12 '15 at 02:24
  • a top-level class is a non inner class, so that means that there is no implicit relationship between the static nested class and the outer class, so instantiating an array dosn't affect the outer class (your generic class) – Tarik Feb 12 '15 at 02:30
  • @Peterxwl I just added an update, please take a look now – Tarik Feb 12 '15 at 02:50
  • 1. "a static Nested class is not considered as an inner class" - false. It *is* an inner static class. 2. "...its of the type Node (**your Top level class**)" - wrong again, a static class *behaves* like a top-level class but it's **not** one. – Nir Alfasi Feb 12 '15 at 03:11
  • 1
    @alfas in the java docs "static nested class is behaviorally a top-level class " thats why i said "considered as", It's clear that it's not one! that's why i quoted the javadocs! but i used that termenology of 'considered' or 'so called' to clarify the issue – Tarik Feb 12 '15 at 03:16
  • 1
    @alfasin *"It is an inner static class."* A static nested class isn't inner. [*"An inner class is a nested class that is not declared static."*](http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.1.3) A nested class can be either inner or static. – Radiodef Feb 12 '15 at 08:15
1

Because with static you create: Node[] next = new Node[R] and with non-static inner-class you create a Node that is associated with an instance of the outer-class, which has a generic type. And creation of generic arrays is forbidden.

But lets back up a few steps: the way to instantiate an inner-class (non-static) is as follows (example):

class TrieST<V> {
    private static final int R = 256;        

    private Node root;      // root of trie
    private int N;          // number of keys in trie
    private TrieST<String> inst = new TrieST<String>(); // must create an instance of the outer class first

    // R-way trie node
    private class Node {
        private Object val;
        private TrieST<String>.Node next =  inst.new Node(); //must use an instance of the outer class to instantiate an object of the inner class
    }

    public TrieST() {
    }
}

Now, if we'll try to change the implementation above from an instance of the inner class to an array, we'll get generic array creation because it's prohibited to create arrays with generic type due to the covariance nature of arrays (Shape[] is super of Triangle[]) which doesn't work well with the invariant nature of generics (List<Object> is not super of List<String>). In "Effective Java" Bloch provides a more detailed explanation if you want to dig in.

If you insist on using an inner-class, you can work around this restriction by using Array.newInstance() which can create array of a type known only at runtime as follows:

private Node[] next = (Node[]) Array.newInstance(Node.class, R);
Nir Alfasi
  • 49,889
  • 11
  • 75
  • 119
  • what if I still want a 'Node[] next' in the non-static inner class? – Peterxwl Feb 12 '15 at 03:40
  • @Peterxwl you can trick the compiler by doing: `private Node[] next = (Node[]) Array.newInstance(Node.class, R);` I'll add it to the answer. – Nir Alfasi Feb 12 '15 at 03:43