I'm reading Joshua Blochs "Effective Java" 2nd edition. Currently I'm at item 22 which describes inner and nested classes but I can't understand what does he mean by this sentence:
Anonymous classes have enclosing instances if and only if they occur
in a nonstatic context.
Can someone give me an example of code and explain what does it exactly do ? I know that if InnerClass is a member of OuterClass its enclosing instance is OuterClass, but in terms of annonymous class it sounds strange to me.
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
System.out.println("hello world");
}
};
}
Here, an anonymous class instance is created from a static context. So it doesn't have any enclosing instance.
public class Foo {
public void bar() {
Runnable r = new Runnable() {
#Override
public void run() {
System.out.println("hello world");
}
};
}
private void baz() {
}
}
Here, an anonymous class instance is created from an instance method. So it has an enclosing instance. The run() method could call baz() or Foo.this.baz(), thus accessing a method from this enclosing instance.
The effect is the same as for non-anonymous inner classes. In essence, it means:
class Outer {
void bar() {
System.out.println("seems you called bar()");
}
void foo() {
(new Runnable() {
void run() {
Outer.this.bar(); // this is valid
}
}).run();
}
static void sfoo() {
(new Runnable() {
void run() {
Outer.this.bar(); // this is *not* valid
}
}).run();
}
}
Because you cannot give the static modifier to anonymous classes, the static property is always inherited from the context.
Related
This question already has answers here:
Java superclass calls subclass method
(5 answers)
Closed 1 year ago.
Why does method() call the overridden subclass method2 instead of method2 in the BaseClass?
public class BaseClass {
public void method(){
System.out.println("method() called");
method2();
}
public void method2(){
System.out.println("method2() called");
}
}
public class ChildClass extends BaseClass {
public void method2(){
System.out.println("method2() from BaseClass");
}
}
public class Main {
public static void main(String[] args) {
ChildClass obj = new ChildClass();
obj.method();
}
}
This is the concept of Runtime polymorphism (Dynamic Method Dispatch). Because you are assigning the object (instance) of ChildClass to obj reference variable, it will call the method of child class.
Always the method of the class whose instance is created gets called first. If that method is not present in that particular child class, then the parent's inherited method gets called.
If you come from the C++ corner:
all instance methods (non-static) in Java are virtual.
All class methods (static) are NOT.
This is why your case happens.
This is also, why the Java compiler will complain (warn) that if you access a static method via an object, that you should call via the distinct class, because calls to the "static method of a object" could be ambiguous, because it could be two static methods with the same signature that get called.
Extending your Example:
package stackoverflow.staticcalls;
public class BaseClass {
public void method() {
System.out.println("method() called");
method2();
}
public void method2() {
System.out.println("method2() called");
}
static public void sayHello() {
System.out.println("BaseClass.sayHello()");
}
}
and
package stackoverflow.staticcalls;
public class ChildClass extends BaseClass {
public void method2() { // compiler warning: The method method2() of type ChildClass should be tagged with #Override since it actually overrides a superclass method
System.out.println("method2() from BaseClass");
}
public void originalCallToBaseMethod2() {
super.method2(); // will run BaseClass.method2()
}
static public void sayHello() {
System.out.println("ChildClass.sayHello()");
}
}
and
package stackoverflow.staticcalls;
public class Main {
public static void main(final String[] args) {
final ChildClass obj = new ChildClass();
System.out.println("\nCalling obj.method(); ...");
obj.method();
System.out.println("\nCalling obj.sayHello(); ...");
obj.sayHello(); // compiler warning: The static method sayHello() from the type ChildClass should be accessed in a static way
System.out.println("\nCalling ChildClass.sayHello(); ...");
ChildClass.sayHello(); // the proper call
System.out.println("\nCalling BaseClass.sayHello(); ...");
BaseClass.sayHello(); // but you can also explpicitly call the other method
System.out.println("\nCalling obj.originalCallToBaseMethod2(); ...");
obj.originalCallToBaseMethod2(); //
}
}
Here you see the examples to what I said.
Note: In the last call in Main.main() we still can call BaseClass.method2(), but not directly. We have to be within ChildClass to do that, and it's done via the super keyword/reference.
A little off-topic note, to complete addressing patterns:
If you're inside an inner class and need to call to a overshadowed name in the outer class, you can use Outer.this.method():
package stackoverflow.staticcalls;
import stackoverflow.staticcalls.OuterInner.Outer.Inner;
public class OuterInner {
class Outer {
void method() {
System.out.println("OuterInner.Outer.method()");
}
class Inner {
void method() {
System.out.println("OuterInner.Outer.Inner.method()");
}
void callOuter() {
Outer.this.method();
}
}
}
public static void main(final String[] args) {
final OuterInner oi = new OuterInner();
final Outer outer = oi.new Outer();
final Inner inner = outer.new Inner();
System.out.println("\nCalling inner.method(); ...");
inner.method();
System.out.println("\nCalling inner.callOuter(); ...");
inner.callOuter();
}
}
As I have gone through some of the programming sites which explains anonymous inner class in java.
But, I still have doubts that how it really works and on the conclusions which I made below.
Here is code :
public class Implementing {
public static void main(String[] args) {
SuperClass a = new SuperClass() { // ... 1st
public void call () { // ... 2nd
System.out.println("Method call");
}
public void call2 () { // creating new method in anonymous inner class
System.out.println ("New call2 Method in Anonymous Class");
}
};
a.call(); // ... 3rd
try {
a.getClass().getMethod("call2",null).invoke(a,null); // ... 4th
} catch (Exeception e) {
System.out.println(e);
}
}
}
class SuperClass {
public void call() {
System.out.println("Super Class");
}
}
What I understand is this :
At 1st :-
Anonymous inner class is sub-class.
we are creating instance of anonymous inner class as well as we
are extending the class SuperClass.
we are running constructor of SuperClass, but we created object
of anonymous class of type SuperClass.
polymorphism at work.
Actually, we are doing this at 1st :
* 1. anonymousClass extends SuperClass
* 2. anonymousClass a = new SuperClass()
* 3. { }; // our anonymous class field
At 2nd : - overrides SuperClass call Method().
At 3rd : - calling call() Method by object 'a'.
At 4th : - accessing new method call2() Method in anonymous inner class.
So, my these conclusions are right or wrong ? If wrong why they are wrong ? Please Explain.
Your code creating an Anonymous Class:
public static void main(String[] args) {
SuperClass a = new SuperClass() { // ... 1st
public void call() {
System.out.println("Method call");
}
public void call2() {
System.out.println("New call2 Method in Anonymous Class");
}
};
...
}
is the same as this code creating a Local Class:
public static void main(String[] args) {
class LocalClass extends SuperClass {
public void call() {
System.out.println("Method call");
}
public void call2() {
System.out.println("New call2 Method in Anonymous Class");
}
}
SuperClass b = new LocalClass();
...
}
except that the class is unnamed, aka anonymous.
Since you're doing this from a static method, and you're not using any local variables, they are both the same as this Static Nested Class:
static class NestedClass extends SuperClass {
public void call() {
System.out.println("Method call");
}
public void call2() {
System.out.println("New call2 Method in Anonymous Class");
}
}
If you look at the compiled .class files, you'll see they are all just classes:
SuperClass.class <-- Your superclass
Implementing.class <-- Your main class
Implementing$1.class <-- Anonymous class
Implementing$1LocalClass.class <-- Local class
Implementing$NestedClass.class <-- Nested class
In Java, I use an anonymous class inside a class A which extends B. How can I access to B from this anonymous class? I can't use the keyword super, because this means super class of the anonymous class, not super class of A.
public class A {
void foo() {
System.out.println("Good");
}
}
public class B extends A {
void bar() {
Runnable r = new Runnable() {
#Override
public void run() {
foo(); // Bad: this call B.foo(), not A.foo()
// super.foo(); // Bad: "Method foo is undefined for type Object"
}
};
r.run();
}
#Override
void foo() {
System.out.println("Bad");
}
}
In run, you could change foo() to B.super.foo(); when I changed that and then ran B.bar() I get Good.
In such a case, you need to qualify this to capture the outer class, B
B.this.foo()
Or, in your case, as you want the super class, use
B.super.foo()
Relevant parts of Java Language Spec:
Qualified this
Accessing superclass members with super
Please call as fallows:
B.super.foo();
After this change B class looks as follows:
public class B extends A {
public static void main(String[] args) {
new B().bar();
}
void bar() {
Runnable r = new Runnable() {
#Override
public void run() {
B.super.foo(); // this calls A.foo()
}
};
r.run();
}
#Override
void foo() {
System.out.println("Bad");
}
}
Imagine we have a class:
class A {
public void m() {
System.out.println("A - > m()");
}
}
...and I want to override the method m on class creation without making a second subclass B to extend A.
public static void main(String[] args) {
A a = new A() {
#Override
public void m() {
System.out.println("Override - > m()");
new Thread(new Runnable() {
#Override
public void run() {
// I want to be able to call the super method.
// This is illegal!
A.super.m();
}
}).start();
}
};
a.m();
}
Currently my solution is to create a private method that calls the super.m()
A a = new A() {
private void superMethod() {
super.m();
}
#Override
public void m() {
System.out.println("Overrided - > m()");
new Thread(new Runnable() {
#Override
public void run() {
superMethod();
}
}).start();
}
};
a.m();
I want to know why I am not able to write A.super.m() and if there another way to perform this task.
I want to know why I am not able to write A.super.m()...
This is because A is in fact not a directly enclosing class. The directly enclosing class of the Runnable is new A() { ... } which is an anonymous subclass of A.
In other words, if you would have had
class A extends Base {
new Runnable() { ... }
}
then A.super would have worked, but now you have
class <Anonymous subclass of A> extends A {
new Runnable() { ... }
}
which means that something like A.super isn't possible, since there's no syntax for <Anonymous subclass of A>.super.m.
...and, is there another way to perform this task.
The way you've solved it is reasonable in my opinion. Another way would be to create a local subclass of A (just to introduce an identifier to use in ____.super.m) as follows:
public static void main(String[] args) {
class SubA extends A {
#Override
public void m() {
System.out.println("Override - > m()");
new Thread(new Runnable() {
#Override
public void run() {
SubA.super.m();
// ^^^^ we now have a name of the directly enclosing class
}
}).start();
}
}
A a = new SubA();
a.m();
}
Writing A.super.m(), suppose that A has a superclass with a m method.
But in your code, you don't specify a superclass, and by default, the only superclass you have is Object.
But Object class doesn't have a 'm' method, so you could not call it.
A good way to do something like that is to use design pattern, like decorator.
I don't think there would be a simpler way to do it other than the way you already have it.
The problem is that the anonymous class A itself (not the base class A) cannot be referenced inside the Runnable class. The anonymous class is represented as something like package.A$1 when compiled to its own class file. For example, when you call superMethod inside the run of the thread, the following bytecode is executed:
getfield mypackage/Test$1$1/this$1 Lmypackage/Test$1;
invokestatic mypackage/Test$1/access$0(Lmypackage/Test$1;)V
In order to reference its base class A, there is no reference to this inner class instance so that you call the super.m() expression.
public class Application {
public static void main(String[] args) {
final class Constants {
public static String name = "globe";
}
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
System.out.println(Constants.name);
}
});
thread.start();
}
}
Compilation Error: The field name cannot be declared static in a non-static inner type, unless initialized with a constant expression
Solution to this?
Java does not let you define non-final static fields inside function-local inner classes. Only top-level classes and static nested classes are allowed to have non-final static fields.
If you want a static field in your Constants class, put it at the Application class level, like this:
public class Application {
static final class Constants {
public static String name = "globe";
}
public static void main(String[] args) {
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
System.out.println(Constants.name);
}
});
thread.start();
}
}
From the JLS section 8.1.3:
Inner classes may not declare static members, unless they are constant variables (ยง4.12.4), or a compile-time error occurs.
So you're fine if you just make the variable final:
public class Application {
public static void main(String[] args) {
final class Constants {
public static final String name = "globe";
}
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
System.out.println(Constants.name);
}
});
thread.start();
}
}
Of course this won't work if you need to initialize it with a non-constant value.
Having said all of this, it's an unusual design, IMO. It's very rare to see a named local class at all, in my experience. Do you need this to be a local class? What are you trying to achieve?