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