Neeraj Kumar
Neeraj Kumar

Reputation: 89

How Generic Typed Array not allowed in Java (SE8) works?

I was trying to implement an R-way Trie Symbol Table, but meanwhile the implementation I have faced an unusual issue or I was not able to wrap head around this. Let me explain the issue I am facing:

package edu.nraj.dsalgo.rwaytrie;

public class RWayTrieST<Value> {
    private static final int R = 256; 
    private Node root = new Node();

    private class Node{
        private Object value;
        private Node[] next = new Node[R];
    }

    public static void main(String[] args) {

    }

}

in this code block one can clearly see that I am creating an internal private class

private class Node{ ... }

But now the java se8 compiler shows an issue for the

private Node[] next = new Node[R];

saying that arrays of generic types not allowed. Node which I already happen to know that Java doesn't allow a generic type array.

The generic type array issue hint screenshot

But here the compiler stop throwing the error if I make this Private Node class a static class.

package edu.nraj.dsalgo.rwaytrie;

public class RWayTrieST<Value> {
    private static final int R = 256; 
    private Node root = new Node();

    private static class Node{
        private Object value;
        private Node[] next = new Node[R];
    }

    public static void main(String[] args) {

    }

}

No issue after adding static to Node class screenshot

Can someone please explain this behavior down to its root cause so I can wrap my head around this.

Upvotes: 2

Views: 149

Answers (3)

NoDataFound
NoDataFound

Reputation: 11949

If think the reason lies in the hidden details:

public class RWayTrieST<Value> {
    private static final int R = 256; 
    private Node root = new Node();    
    private class Node{
        private Object value;
        private Node[] next = new Node[R];
    }   
}

Is actually:

public class RWayTrieST<Value> {
    private static final int R = 256; 
    private Node root = new Node();    
    private class Node<Value>{
        private final RWayTrieST<Value> $PARENT;
        private Object value;
        private Node[] next = new Node[R]; // and so Node is Node<Value> by this.
    }   
}

$PARENT is a synthetic field (although that's not the exact name) the compiler adds to allow call to the parent non static methods. If the parent class contains generics, then the type of the $PARENT is the RWayTrieST<Value>.

And since Value is from the class declaration, it needs to be passed down to instance as well:

private static class Node<Value>

And so on in declaration, hence new Node<Value>[R].

If you want to fix that:

  • If you don't need to call the parent' non static methods, then use static class as you were doing.
  • Use a ArrayList.
  • If you need to call parent static method, then make the Node class static and add a private field RWayTrieST<?>. You will probably have other problems.

Upvotes: 0

Sweeper
Sweeper

Reputation: 270980

If you make your inner class static, it basically makes Node not care about the generic parameter Value. If Node is not static, Node then "belongs to instances of RWayTrieST", just like any other non-static member. This means that the Node of an instance of RWayTrieST<String> will be a different type than the Node of an instance of RWayTrieST<Integer>.

This is as if Node itself has a generic parameter, isn't it? RWayTrieST<String>.Node and RWayTrieST<Integer>.Node are entirely different types (at compile time, at least), just like ArrayList<String> and ArrayList<Integer>.

According to here, you can't create arrays of type with generic parameters, because something like this could happen if this were allowed:

RWayTrieST<String> foo = ...;
RWayTrieST<Integer> bar = ...;
Object[] stringArray = foo.new Node<String>[];  // compiler error, but pretend it's allowed
stringArray[0] = foo.new Node<String>();   // OK
stringArray[1] = bar.new Node<Integer>();  // An ArrayStoreException should be thrown,
                                            // but the runtime can't detect it, because generic types are erased.

Upvotes: 0

Leo Aso
Leo Aso

Reputation: 12463

In the simplest way I can put this, when you make a class generic, all its inner classes are also generic because they share the outer class information. As you probably already know, the difference between

public class RWayTrieST<Value> {
    private class Node {}
}

and

public class RWayTrieST<Value> {
    private static class Node {}
}

is that, in the former, Node is an inner class and instances of Node have to be tied to instances of RWayTrieST. So if you have foo = New RWayTrieST<String>() and you have bar = foo.new Node(), then to the compiler, bar's type is effectively RWayTrieST<String>.Node because the generic information of the outer class is necessary.

In the latter however, instances of Node are not tied to instances of RWayTrieST because it is a static inner class. So Node does not share the outer class generic info which means it is not generic.

Upvotes: 1

Related Questions