Static nested class vs non static error? [duplicate] - java

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.

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

How to create object of second level inner class? [duplicate]

This question already has answers here:
What causes error "No enclosing instance of type Foo is accessible" and how do I fix it?
(11 answers)
Closed 8 years ago.
I'm new to Java.
My file A.java looks like this:
public class A {
public class B {
int k;
public B(int a) { k=a; }
}
B sth;
public A(B b) { sth = b; }
}
In another java file I'm trying to create the A object calling
anotherMethod(new A(new A.B(5)));
but for some reason I get error: No enclosing instance of type A is accessible. Must qualify the allocation with an enclosing instance of type A (e.g. x.new B() where x is an instance of A).
Can someone explain how can I do what I want to do? I mean, do I really nead to create instance of A, then set it's sth and then give the instance of A to the method, or is there another way to do this?
Outside the outer class, you can create instance of inner class like this
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
In your case
A a = new A();
A.B b = a.new B(5);
For more detail read Java Nested Classes Official Tutorial
In your example you have an inner class that is always tied to an instance of the outer class.
If, what you want, is just a way of nesting classes for readability rather than instance association, then you want a static inner class.
public class A {
public static class B {
int k;
public B(int a) { k=a; }
}
B sth;
public A(B b) { sth = b; }
}
new A.B(4);
Interesting puzzle there. Unless you make B a static class, the only way you can instantiate A is by passing null to the constructor. Otherwise you would have to get an instance of B, which can only be instantiated from an instance of A, which requires an instance of B for construction...
The null solution would look like this:
anotherMethod(new A(new A(null).new B(5)));

inner class access of enclosing class members

I will be amazed if anyone can answer this question. I am a beginner struggling immensely with the syntax and logic of nested classes in Java. If you run the following program, 'a' will print instead of 'b'. Why?
class MainClass
{
public static void main(String[] args)
{
Outer OuterRefVar_a = new Outer('a');
Outer OuterRefVar_b = new Outer('b');
OuterRefVar_a.InnerTypeMember = OuterRefVar_a.new Inner();
OuterRefVar_b.InnerTypeMember = OuterRefVar_a.InnerTypeMember;
OuterRefVar_b.InnerTypeMember.set_innerChar_to_outerChar();
System.out.println(OuterRefVar_b.InnerTypeMember.innerChar);
}
}
class Outer
{
char outerChar;
Outer(char outerChar)
{
this.outerChar = outerChar;
}
class Inner
{
char innerChar;
void set_innerChar_to_outerChar()
{
innerChar = outerChar;
}
}
Inner InnerTypeMember;
}
That happens because while you have set the InnerTypeMember reference of object of A onto B..
OuterRefVar_b.InnerTypeMember = OuterRefVar_a.InnerTypeMember;
The inner object of A still has a reference to it's original Outer object A and will reference its member variables. Java implements inner classes by giving the object a secret reference to "Outer.this" which doesn't change simply by setting the InnerTypeMember on the other instance.
For example, if you had a InnerTypeMember variable within a completely different class, calling set_innerChar_to_outerChar() would still be expected to find Outer.outerChar on the object for which the inner class was original constructed.

What does it mean if a static inner class is also final? [duplicate]

This question already has answers here:
What is the point of "final class" in Java?
(24 answers)
Closed 7 years ago.
public class ClassWithInnerClass {
int a = 10;
public static void outer(){
System.out.println("In method of outer class");
}
final static class Inner{
int b = 20;
public void innermethod(){
System.out.println("In method of inner class");
System.out.println("Inner class variable b = "+b);
}
}
}
In the above code, i have an outer class and then there is a static nested class with the non-access specifier 'final'.Does this make this class similar to "Constant" variables?
No, it's not similar to constant variable. It means that you can't create a sub-class of this nested class (Inner). This is similar to using the final keyword in top level (i.e. not nested) classes.
final on classes mean that they cannot be extended. It's no different from having final on a class defined at the top level.
final on classes is not the same as final on variables. final has different meanings based on where you use it. In general it describes a sort of immutability:
On variables, it means that you cannot change its value once initialized.
On methods, it means that once defined, it cannot be overridden in a subclass.
On classes, it means that the class itself cannot be subclassed.

Why static nested class?

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

Categories

Resources