Reputation: 335
So I got this weird scenario that works fine but doesn't make any sense as to why it works. From my experience in C++, I'm very sure that this will not work at all and will throw an error during compilation.
public class Practice {
private void testFunction() {
System.out.println("working fine");
System.out.println("testFunc: " + this);
}
public void start() {
System.out.println("start: " + this);
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("run: " + this);
testFunction();
}
}).start();
}
}
// Inside main
Practice practice = new Practice();
practice.start()
Output
start: com.company.Practice@5e2de80c
run: com.company.Practice$1@6720b744
working fine
testFunc: com.company.Practice@5e2de80c
WHY!? why did this work? How am I able to call testFunction()
from a Runnable? Shouldn't I create a new instance and then call that function like Practice p = new Practice(); p.testFunction()
? How does Java know that testFunction()
is part of Practice
class and not Runnable
?
And also, how come the value of this
in testFunction()
is same as start()
? Shouldn't it be same as run()
?
Upvotes: 1
Views: 82
Reputation: 272895
You can access testFunction
is because you are using the simple name of testFunction
. If instead you did:
this.testFunction();
Then you wouldn't be able to access it.
When you call a method with its simple name, everything that is in scope is considered to resolve the method. (See JLS 6.5.7.1) testFunction
is definitely in scope inside the Runnable
, because it is inside Practice
.
To call testFunction
, you still need an instance of Practice
, right? Well, the compiler generates some code that passes the instance of Practice
on which start
is invoked (i.e. what this
means in start
) to the instance of Runnable
that you are creating. Specifically, it creates a non-static inner class
class Practice {
public void start() {
...
new Thread(new SomeGeneratedRunnable()).start();
}
// not the actual name of the class. The actual name is Practice$1
private class SomeGeneratedRunnable implements Runnable {
public void run() {
System.out.println("run: " + this);
testFunction();
}
}
}
You can think of this as:
class Practice {
public void start() {
...
new Thread(new SomeGeneratedRunnable(this)).start();
}
}
class SomeGeneratedRunnable implements Runnable {
private final Practice outer;
public SomeGeneratedRunnable(Practice outer) {
this.outer = outer;
}
public void run() {
System.out.println("run: " + this);
outer.testFunction();
}
}
Now you should see why this
inside testFunction
can't possibly be the same as the this
inside the Runnable
. The this
inside Runnable
is an instance of SomeGeneratedRunnable
(or Practice$1
), but this
inside testFunction
is an instance of Practice
.
Upvotes: 1
Reputation: 262734
You have an "inner class" here.
Instances of the inner class (your anonymous subclass of Runnable) will contain references to the containing outer classes they were created from (here an instance of Practice) and can call methods on those outer instances.
Your testFunction();
gets compiled to something like this.$myOuterInstance.testFunction();
How does Java know that testFunction() is part of Practice class and not Runnable?
The compiler resolves this by first looking at the inner class then going to the outside scope, which not only includes the containing class, but for example also any effectively final local variables you might have in that start
method.
This is similar to how variables and fields are resolved as well (with some shadowing rules when you have name conflicts).
And also, how come the value of this in
testFunction()
is same asstart()
? Shouldn't it be same asrun()
?
No. testFunction
is invoked on the instance of Practice
(as is start
).
run
is invoked on the instance of your Runnable Practice$1
.
Upvotes: 2