Local class instance creation expression in a static-context - java

The JLS 15.9.2 tells us how to determine an enclosing instance:
Let C be the class being instantiated, and let i be the instance being created.
If C is an inner class, then i may have an immediately enclosing
instance (§8.1.3), determined as follows:
[...]
If C is a local class, then:
If C occurs in a static context, then i has no immediately enclosing
instance.
Otherwise, if the class instance creation expression occurs in a
static context, then a compile-time error occurs.
Otherwise, let O be the immediately enclosing class of C. Let n be an
integer such that O is the n'th lexically enclosing type declaration
of the class in which the class instance creation expression appears.
The immediately enclosing instance of i is the n'th lexically
enclosing instance of this.
I didn't get what the bolded case means. Let me provide the example I wasn't supposed to be compiled:
class A{
int a;
public static void main (String[] args) throws java.lang.Exception{
class Foo{
void bar(){
}
}
Foo f = new Foo(); //Instance creation expression occured in the static context
}
}
DEMO
What's wrong with that? Couldn't you provide an actual example describing the second point?

You should read these two lines :
If C occurs in a static context, then i has no immediately enclosing instance.
Otherwise, if the class instance creation expression occurs in a static context, then a compile-time error occurs.
Your case is the first case - you defined the class Foo in a static context (the main method), and therefore the instance f has no enclosing instance.
If, however, you'd define the class Foo outside the main method, and try to create the instance of Foo in the main method, you'll get an error, unless you change Foo to be a static class.
class A
{
int a;
class Foo
{
void bar()
{
}
}
public static void main (String[] args) throws java.lang.Exception
{
Foo f = new Foo(); // this should fail
}
}

I guess the following case is meant:
public class A {
public class B { /* ... */ }
public static void createB() {
new B(); // <<=== This should fail
}
}
The marked line should fail, since the inner class B is not static, so it requires en enclosing instance of A. There is no such enclosing instance, since the method createB() is static.
UPDATE: I mistook the question beeing about inner classes in general, which is what my example shows. In the context of Local Classes I also can't interpret the documentation.

Related

Why can't a local class that extends an inner class access the inner class enclosing instance?

(I keep re-reading that question title and thinking about how ridiculous it must look, but I assure you that is the best description of the problem, and I have an actual application where this is the best structure. I swear I'm not crazy.)
Consider the following. Each block is a separate file:
package myPackage;
public class A {
public int i;
public A(int i) {
this.i = i;
}
public class B {
}
}
package myPackage;
import myPackage.A.B;
public class Main {
public static void main(String[] args) {
class C extends B {
public C(A enclosingInstance) {
enclosingInstance.super();
}
public void show() {
System.out.println(A.this.i);
}
}
A myA = new A(2);
C myC = new C(myA);
myC.show();
}
}
Note that the enclosingInstance business is to solve a problem involving intermediate constructor invocations. See "Why can't outer classes extend inner classes?".
I would expect the output to be "2". But instead, I have a compile error on System.out.println(A.this.i);:
No enclosing instance of the type A is accessible in scope
I think the programmatic concept I'm trying to solve is sound: Create a new type of B inside main to give to A that uses things from A that types of B can access.
So what am I doing wrong, or why isn't this possible in java?
EDIT/UPDATE: Note that the same error appears when the code in main is moved to a non-static method. That is to say, I tried moving everything inside of static void main to a new, non-static method of class Main called go(). Then I changed static void main to the single line new Main().go();. The error is in the same spot. So it doesn't seem to be an issue of class C being defined in a static context.
You want A.this to refer to the enclosing instance of the B instance. But why should it? That's not what the syntax means. A.this would mean the enclosing A instance of the C instance, and this does not make sense because C is not an inner class of A.
To make this clearer, here is an example where C is an inner class of A.
public class A {
public int i;
public A(int i) {
this.i = i;
}
public class B {
void foo() {
System.out.println(A.this.i);
}
}
public class C extends B {
C(A a) {
a.super();
}
void bar() {
System.out.println(A.this.i);
}
}
public static void main(String[] args) {
A a1 = new A(1);
A a2 = new A(2);
C c = a1.new C(a2);
c.foo();
c.bar();
}
}
Here C extends B, and both C and B are inner classes of A. Therefore any C has an enclosing A instance, and it also has an enclosing A instance when considered as a B, and these enclosing instances are different (as proved by the fact that foo and bar print different numbers).
So, A.this could not possibly mean what you want it to mean, because it already means something else. I guess the reason why the language designers didn't come up with other syntax to mean the enclosing instance of a super class, is because such syntax would be very complicated, with little pay-off (simple workarounds already exist).
This is absurd code that you should never write for production.
It is, in part, explained in the documentation for Explicit Constructor Invocations
Qualified superclass constructor invocations begin with a Primary
expression or an ExpressionName. They allow a subclass constructor to
explicitly specify the newly created object's immediately enclosing
instance with respect to the direct superclass (§8.1.3). This may be
necessary when the superclass is an inner class.
All this to say that C is a local class (which is an inner class, which is kind of nonsense because if you declare it in a static method there is no enclosing instance) that is a subclass of B but not a nested class of A. As such, there is no enclosing instance. An instance of C does not have an enclosing instance. (Though it would if you declared it in an instance method, but that would be an instance of Main.)
The newly created object's immediately enclosing instance (from JLS) is specified indirectly through a constructor parameter.
You'd have to store it yourself
private A enclosingInstance;
public C(A enclosingInstance) throws CloneNotSupportedException {
enclosingInstance.super();
this.enclosingInstance = enclosingInstance;
}
and since A#i is public, you can access it normally
public void show() {
System.out.println(enclosingInstance.i);
}
With the provided information, I would do this :
public class B {
protected A getOuterInstance() {
return A.this;
}
}
and just let C inherit and use this method. I know you dislike this method but this is the simplest answer I can see. With more information, I would probably propose a design which would try not involving any inner class as this is not a normal use case for inner classes.

Member nested classes in java?

If we have a code...
public class Hello
{
public static void main(String args[])
{
Outer obj=new Outer();
obj.method1();
}
}
class Outer
{
void method1()
{
class Inner
{
}
}
}
I wanted to know,when the Inner class will be loaded by the ClassLoader.
Is it loaded at the time the method1() is called,or at the time when we will be creating its instance?And the class Inner is not performing any operation in method1(),its an empty class.Also,I wanted to know,how to create an instance of inner class in the above example?
Class Outer.Inner will be loaded the first time another class that refers to it as a variable type, method parameter type, method return type, superclass type, type parameter bound, or target type of an initializer or static method, or host class of a static variable reference is loaded. In your example, I would expect it never to be loaded.
Also,I wanted to know,how to create an instance of inner class in the above example?
As it is written, class Outer.Inner is accessible only inside method Outer.method1(), therefore it can be instantiated only within that method. There, you can just use new Inner(). If you want it to be instantiable from elsewhere then move its declaration out of the method body:
class Outer
{
class Inner
{
}
void method1()
{
}
}
That's better form for a named inner class anyway. It will not change when or whether Outer.Inner is loaded.
With that change, you can instantiate Outer.Inner anywhere within a constructor or instance method of Outer via the form new Inner(). If, however, you want to instantiate one from a different class, or in a static method of class Outer, then it's a bit trickier. The important thing to realize is that each instance of Outer.Inner needs an associated instance of Outer. This is determined from context when the instantiation is performed in an instance method of Outer, but if it is performed without such a context then the syntax is:
public static void main(String args[])
{
Outer obj=new Outer();
Outer.Inner inner = obj.new Outer.Inner();
}
Quoting from Section 12.4.1 of the JLS :
A class or interface type T will be initialized immediately before the
first occurrence of any one of the following:
T is a class and an instance of T is created.
A static method declared by T is invoked.
A static field declared by T is assigned.
A static field declared by T is used and the field is not a constant
variable (§4.12.4).
T is a top level class (§7.6) and an assert statement (§14.10)
lexically nested within T (§8.1.3) is executed.
Therefore, a class (regardless of whether is a nested class) will follow the same rules. In your particular case, Inner will not be loaded until an instance of the class is created.
To answer your second question, you can only create an instance of Inner inside method1 since Inner is a method local class whose scope is limited to method1 :
void method1()
{
class Inner
{
public Inner()
{
System.out.println("inner()");
}
}
Inner inner = new Inner();
}
To achieve the inner class, you need object anyway. If you call method1() like this, inner class never be called.
But if you define some object in method1() or public Outer() constructor, you can get result from inner class.

Only super class is initialized even though static field is referenced using sub type

I am doing some research on JAVA initialization process.
Here is a good material for reference:
When a class is loaded and initialized in JVM
On this page there is rule says:
3) If Class initialization is triggered due to access of static field, only Class which has declared static field is initialized and it doesn't trigger initialization of super class or sub class even if static field is referenced by Type of Sub Class, Sub Interface or by implementation class of interface.
I really don't understand the idea. If the static field is referenced by Sub class, then this field of course need to create a sub class object or assigned by a Sub class object.
So, it definitely triggers Sub class initialization.
What's wrong with my interpretation?
EDIT:
It DOES trigger Super Class static initialization.
If the static field is final, and the static final field is initialized when declaring. Then it will neither load the class nor initialize the class, for this static final field is a compile time constant value. Attention: if the static final field is initialized in static block, then this statement does NOT hold anymore.
I think the point is that in a situation like this:
public class Superclass {
public static long INIT_TIME = System.currentTimeMillis();
static {
System.out.println("Initializing Superclass");
}
}
public class Subclass extends Superclass {
static {
System.out.println("Initializing Subclass");
}
}
This code:
long time = Subclass.INIT_TIME;
is actually compiled to:
long time = Superclass.INIT_TIME;
and only "Initializing Superclass" will be printed, even though the source code referred to Subclass.
An example:
class A {
public static int nA = 0;
}
class B extends A {
public static int nB = 1;
}
class C extends B {
public static int nC = 2;
}
Client:
int test = B.nA;
The JVM will initialize only Class A. Not B nor C.
As shown above, when I run the Superclass/Subclass example, on calling Subclass.INIT_TIME,
both the Superclass and Subclass static initializers are getting invoked.
But here it is said that only "Initializing Superclass" will be printed.
Can someone clarify?

Problems understanding static modifier

I am having difficulty understanding what a "static" method and "static" variable is and it is causing me problems with my code. Here is the code I am having difficulty with:
public class Document{
public void NewBlank(){
Resources.openRawResource(R.raw.blank);
}
}
Why do I get the error "Cannot make a static reference to the non-static method Resource.openRawResource(int) from the type Resources"? Why can't I reference a non-static method?
openRawResources is not a static method, it needs to be invoked in an object, not a type. In order to get an instance of Resources you could call getResources in an activity. Then the resulting code would be
Resources resources = myactivity.getResources();
resources.openRawResource(R.raw.blank);
A static method/variable is one that belongs to the class type, and not to the instances/objects of such type.
Cannot make a static reference to the non-static method
This means that for invoking that method you are trying to, you need a reference to an instance of that class.
Here's an example illustrating the difference:
public class Foo{
public static int staticVariable = 5;
public static void methodStatic(){}
public void nonStaticMethod(){}
}
here's how you can use them:
Foo.nonStaticMethod(); //can call static method referring to the class itself without having an instance
Foo f = new Foo();
f.nonStaticMethod(); //you need an instance of a Foo class in order to call a non-static method
For what concern static variables, these are variables that doesn't belong to a single instance of a class, but are shared between all different instances of the same class:
Foo a = new Foo();
Foo b = new Foo();
System.out.println(a.staticVariable); //print 5
System.out.println(b.staticVariable); //print 5
a.staticVariable = 10;
System.out.println(b.staticVariable); //print 10
(Please, look at the example above just to understand the concept of what a static variable is. You'll get the warning "access a static field in a non-static way" because that's not a proper way to access those variables)

Confusion with a class level and instance level

I have the following class:
public class B {
public void print() {
}
public static void main(String[] args) {
B B = new B();
B.print();
}
}
I was wondering why the compiler didn't give an error saying it's not a static method. How will it distinguish between the class level and instance level when we have the object with the same as the class?
Because you are accessing the method on an instance of the class. Incidentally the name of the instance is the same as the class name, but since you don't have a static method with this name, the compiler assumes the correct - i.e. an instance method.
If you define the method to be static, then it will again assume the only possible thing - calling a static method on the B class, because the instance doesn't have such a method.
And ultimately, you can't have both a static and a non-static method with the same name.
The JLS discusses and defines the precedence of scoping rules for such cases:
6.3.2 Obscured Declarations
A simple name may occur in contexts where it
may potentially be interpreted as the
name of a variable, a type or a
package. In these situations, the
rules of §6.5 specify that a variable
will be chosen in preference to a
type, and that a type will be chosen
in preference to a package. Thus, it
is may sometimes be impossible to
refer to a visible type or package
declaration via its simple name. We
say that such a declaration is
obscured.
There are other border cases, where variable can be shadowed or hidden. Example:
class Test {
static int x = 1;
public static void main(String[] args) {
int x = 0;
System.out.print("x=" + x);
System.out.println(", Test.x=" + Test.x);
}
}
Again, the compiler follows the JLS and resolves the name according to the spec. The compiler is not smart and does not "figure out" anything, it just follows the spec.
Your question is about the print() method? It works because your variable name is "hiding" the type name, so when you do B.print() it looks at the variable B, which is an instance of class B.
You really shouldn't be calling your variable the same name as the class, at least not with the same case. Things would be clearer for you if you renamed the variable, even if just to 'b'. So:
public class B{
public void print(){
}
public static void main(String[] args){
B b = new B();
b.print(); // This works
B.print(); // this fails
}
}
Because in that example, print() is a instance method. It is being called from an instance of the class B. If your main looked like this:
public static void main(String[] args){
print():
}
then print() would have to be a static method.
It also doesn't matter that your instance is named the same as the class. Since print() is an instance method, it will expect it to be called from an object. It just so happens that you have an object named B that is an instance of class B. Thus there is no problem.
I guess class names and instance variables do not share the same namespace; to the compiler, it is clear that B.print() is being called on the instance and not on the class.
No need to say that this is confusing and should be avoided in real code ;)

Categories