Why static nested class? - java

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() {
}
}

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) .

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);

Related

How is inner class initialized

Below is a piece of code which provides an example of using inner class in Java.
I would like to know how the inner class instance iterator is created and linked to the arrayOfInts array.
DataStructureIterator iterator = this.new EvenIterator();
I understand that the 'ds' instance is created by the constructor of DataStructure class, but the iterator instance is of DataStructureIterator type. It seems not quite reasonable that a DataStructureIterator instance can be constructed by a constructor of another class.
Full code here:
public class DataStructure {
// Create an array
private final static int SIZE = 15;
private int[] arrayOfInts = new int[SIZE];
public DataStructure() {
// fill the array with ascending integer values
for (int i = 0; i < SIZE; i++) {
arrayOfInts[i] = i;
}
}
public void printEven() {
// Print out values of even indices of the array
DataStructureIterator iterator = this.new EvenIterator();
while (iterator.hasNext()) {
System.out.print(iterator.next() + " ");
}
System.out.println();
}
interface DataStructureIterator extends java.util.Iterator<Integer> { }
// Inner class implements the DataStructureIterator interface,
// which extends the Iterator<Integer> interface
private class EvenIterator implements DataStructureIterator {
// Start stepping through the array from the beginning
private int nextIndex = 0;
public boolean hasNext() {
// Check if the current element is the last in the array
return (nextIndex <= SIZE - 1);
}
public Integer next() {
// Record a value of an even index of the array
Integer retValue = Integer.valueOf(arrayOfInts[nextIndex]);
// Get the next even element
nextIndex += 2;
return retValue;
}
}
public static void main(String s[]) {
// Fill the array with integer values and print out only
// values of even indices
DataStructure ds = new DataStructure();
ds.printEven();
}
}
This is a question from the Oracle's Java documents, here is the source: https://docs.oracle.com/javase/tutorial/java/javaOO/innerclasses.html
From your Q:
It seems not quite reasonable that a DataStructureIterator instance can be constructed by a constructor of another class.
In your code, the instance of the DataStructureIterator is actually not created by the DataStructure constructor. It is created whenever the DataStructure instance method printEven is called:
public void printEven() {
// Print out values of even indices of the array
DataStructureIterator iterator = this.new EvenIterator();
// Omitted for brevity
}
But it could also be created in the constructor as a field or set with a default value e.g.:
public class DataStructure {
private DataStructureIterator iterator = new EvenIterator();
// Omitted for brevity
}
If your inner class (EventIterator) was public, it could also be created "outside" of DataStructure:
// In some other Java class
DataStructure ds = new DataStructure();
DataStructure.EventIterator iterator = ds.new EventIterator();
The key thing is that outer class (DataStructure) instance is created first.
As the docs note:
To instantiate an inner class, you must first instantiate the outer class
This is because the instance of an inner class is always tied to the outer/parent class.
From your Q:
I would like to know how the inner class instance iterator is created and linked to the arrayOfInts array.
The key thing is that an instance of a inner non-static class is that always has access to the parent instance's methods/fields.
As the docs also state (emphasis added):
As with instance methods and variables, an inner class is associated with an instance of its enclosing class and has direct access to that object's methods and fields.
As such, the instance of the DataStructureIterator can access the DataStructure field arrayOfInts.
But note that if you created a separate DataStructure instance e.g. ds2, then the DataStructureIterator instance within ds2 will have access to ds2.arrayOfInts but it won't have access to the ds.arrayOfInts. In your code the values of arrayOfInts will be the same but the instances are actually different (try allowing arrayOfInts to be set via the DataStructure constructor).
If DataStructureIterator was defined as a nested static class, then it won't automatically have access to arrayOfInts and it would need arrayOfInts passed in as an arg to its constructor or to its method.
You can think of inner non-static classes as a logical grouping of code for the parent (DataStructure class) and the docs linked above outline good reasons when to use them. But in general don't use them unless you know what you're doing.
An inner class is instantiated the same as any other class is, just the scope of the class (and thus its instances) is different.
So yes, your inner class instance is created by a call inside the outer class method (not the constructor in the case of your example, but the printEvent method).
I don't know what you mean with the "ds instance is created by the constructor of DataStructure". If you mean that the class is initialised by the constructor, no.
A non-static inner class is best thought of as follows (because.. they really are exactly like this, at the class level):
They have a secret (invisible) final field of the outer type's type.
All constructors, even the default (empty) one have a secret additional parameter of the same type, and they all have an additional extra syntax sugar line at the top, in the vein of this.theFinalFieldIMentionedEarlier = thatParameter;.
Calling new InnerClass() will silently pass this along as that parameter.
If no this that fits is available, you must pass the parameter yourself. However, instead of writing new InnerClass(instanceOfOuter), you write instanceOfOuter.new InnerClass() instead. The effect is identical, and at the class level, there is no difference.
Generics is rather wonky; if the outer has it, oof.
As a consequence:
Do not use non-static inner classes unless you really know what you are doing and you understand and want this weird field. If you don't, or if you feel this is confusing (and it is; most java programmers do not know this is how it works), you might want to consider making your inner class static, and making that field explicit - i.e. handrolling this functionality. It's not particularly complicated (requires 1 field, 1 constructor parameter, and 1 this.outer = outer; statement in the constructor, not exactly a boatload of boilerplate), and clears up rather a lot.
So how does it work - exactly as if it had that field. Because, that's exactly what javac does. inner classes mostly don't exist at the class level, they just become top level classes with funky names (Outer$Inner), a boatload of synthetic methods to bridge private methods together. In newer JVMs, there's the nestmates system that avoids some of those bridges, but they're still individual top-level classes after javac is done with it.
In other words, this:
class Outer {
class Inner {
public Inner(int example) {}
}
void test() {
new Inner();
}
}
is virtually identical to:
class Outer {
static class Inner {
private final Outer outer;
public Inner(Outer outer, int example) {
this.outer = outer;
}
}
void test() {
new Inner(this);
}
}

Static nested class vs non static error? [duplicate]

This question already has answers here:
Java inner class and static nested class
(28 answers)
Closed 5 years ago.
I used the following piece of code, which gave me an error at the point indicated:
class LinkedList{
class pair{
Integer petrol;
Integer distance;
public pair (Integer a, Integer b){
petrol = a;
distance = b;
}
}
public static void main(String args[]){
pair[] circle = {new pair(4,6), new pair(6,5), new pair(7,3), new pair(4,5)}; // error at first element of array circle!!!!!!!
}
}
I then rectified it to this and the error dissapeared!
class LinkedList{
static class pair{ // changed to static!!!
Integer petrol;
Integer distance;
public pair (Integer a, Integer b){
petrol = a;
distance = b;
}
}
public static void main(String args[]){
pair[] circle = {new pair(4,6), new pair(6,5), new pair(7,3), new pair(4,5)}; //error gone!
}
}
My question is why did the error even appear in the first place?
ERROR: No enclosing instance of type LinkedList is accessible. Must
qualify the allocation with an enclosing instance of type LinkedList.
In case 1, pair is a member of LinkedList. Meaning that you can access pair through only LinkedList and not directly just like any varaible or method of that class.
A nested class is a member of its enclosing class. Non-static nested classes (inner classes) have access to other members of the enclosing class, even if they are declared private. Static nested classes do not have access to other members of the enclosing class.
To instantiate an inner class, you must first instantiate the outer class. Then, create the inner object within the outer object with this syntax:
OuterClass.InnerClass innerObject = outerObject.new InnerClass();
However in case 2, pair is just like any another top level class and just grouped to maintain the relation. It is not at all a member of outer class. You can access it directly.
Note: 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.

Implementation of Collection and Iterator interface as inner class

I'm trying to implement a Collection interface with Iterator as inner class. An ArrayCollection class that implements collection has a generic array (is it a correct way to say that class members are generic?).
a screenshot from IDE
public class ArrayCollection<T> implements Collection<T> {
private T[] m = (T[])new Object[10];
However when I'm implementing method next() for an Iterator interface I keep getting an Incompatible types error. However if make an ArrayIterator a non-generic class compiler doesn't have problems with array typecasting anymore.
an error screenshot from IDE
private class ArrayIterator<T> implements Iterator<T> {
private int cursor = 0;
#Override
public boolean hasNext() {
return this.cursor >= ArrayCollection.this.size();
}
#Override
public T next() {
return ArrayCollection.this.m[cursor++];
}
}
So I have few questions:
How come the compiler can define the T[] m array type if I make an ArrayIterator non-generic?
Is it only the inner classes that implement/extend generic interfaces/classes can be non-generic?
You named your type variables similarly, ArrayIterator<T> has a different T than ArrayCollection<T>'s T is.
You can just remove the <T> from ArrayIterator (since it is a non-static inner class) and just have Iterator use the T from the parent class:
private class ArrayIterator implements Iterator<T> {
This will fix the compile issue and your code.
In the second example, you are dealing with two different type variables. The outer class and the inner class each define a variable T, but they are not the same. There are several ways to solve this, one is to remove the T from the inner class declaration:
private class ArrayIterator implements Iterator<T> {
Now you are referencing the outer T only, without introducing a separate inner T
However, I would personally prefer to make the inner class static, in which case you can't use the outer type's variables. In this case you'd write
private static class ArrayIterator<T> implements Iterator<T> {
If you do this, you need to pass the type variable from the outer type to the inner:
return new ArrayIterator<T>();
The main difference is that a class that defines a type parameter shadows any existing type parameter of the same name, which leads to that strange error message.
Finally let me add that it's usually not a good idea to implement a Collection from scratch. Instead, you might want to extend AbstractCollection or AbstractList. This will let you focus on your core algorithm, but provide you with all the boilerplate methods for free.

Confusion with subclasses and superclasses

Master test = new Inner();
System.out.println(test.getClass());
In the above example the Inner class extends the Master class, but what I'm confused about is that test.getClass() returns Inner, but isn't test really of the type Master? Other than the constructor no methods/properties can be used from the Inner class, only what's in the Master class. Furthermore the constructor for Inner actually sets properties exclusive to Inner, but somehow these properties don't exist in test even though it uses the constructor -- which doesn't seem like it should work.
For example if define the classes as:
public class Master {
public int number = 0;
public Master() {
number = 9;
}
}
public class Inner extends Master {
public int innerNumber = 0;
public Inner() {
number = 1;
innerNumber = 2;
}
}
test will use Inner's constructor which sets innerNumber, but test.innerNumber doesn't even exist because innerNumber isn't apart of the Master type. Also, test.getClass() says it's of the Inner type, not Master.
Object.getClass() returns the class object of the dynamic type of the object, not the static type (the type of the variable or attribute you declared it).
Hence new Inner().getClass() returns Inner.class, new Master().getClass() returns Master.class no matter what the type of the variable is that holds the reference.
Question 1:
Master test = new Inner();
The above line indicates that get method implementation's from Inner class (ovveriding). So Inner classes getClass() method calls.
Question 2:
test.innerNumber
Inheritance happens from Parent to Child. innerNumber is a property of Inner(child). Master(Parent) won't get it.

How is this not a Java visibility violation

It is a simple implementation of linked list to split one list into two sublists. Other details have been discarded for simplicity
class SList {
private head;
Object item;
public void split_list(SList list1, SList list2) {
list1.head = this.head;
// Some other stuff
}
}
isn't it a visibility violation to do assign list1.head? To my surprise, I tried and it worked fine
The private modifier means a member can only be accessed by the class itself, it's not restricted to an instance of that class. Also see the documentation
An instance of a class always has complete access to all members of other instances of the same class, regardless of their visibility. private means private to this class, not to this object.
As per JLS 6.6.8:
A private class member or constructor is accessible only within the body of the top level class (ยง7.6) that encloses the declaration of the member or constructor.
It's the same class.
The modifier private of the member head means private to the class SList, not private to an instance of SList (defined in JLS 6.6.8, http://java.sun.com/docs/books/jls/third_edition/html/names.html#6.6.8).

Categories

Resources