0

I was having a bizarre time trying to reference instance variables of a class within a runnable. Here was my (simplified) code:

public class Hello{

    private Boolean truth;

    public Hello(Boolean truthParam){

        this.truth = true;

        Runnable myRunnable = new Runnable(){
            public void run(){
                if (this.truth){
                    System.out.println("i declare the truth!");
                }else{
                    System.out.println("come back tomorrow");
                }

            }
       };
    }
}

When I compiled this code, I got "error: cannot find symbol ... symbol: variable truth"

I thought perhaps within the context of the Runnable, "this" was no longer the main class object; however, I printed "this" to the console, and it was an instance of Hello. Which begs the question: why was I unable to access my instance variable via this?

I then removed the "this" from if (this.truth), converting it to if (truth); needless to say, this compiled and ran. I am quite confused by this behaviour I cannot explain. Perhaps I am simply misunderstanding the intricacies of "this"...

GregarityNow
  • 707
  • 1
  • 11
  • 25
  • 5
    It's because, 'this' is the runnable. https://stackoverflow.com/questions/1816458/getting-hold-of-the-outer-class-object-from-the-inner-class-object#1816462 – matt May 16 '18 at 12:04
  • @matt while that is true the problem for the actual error message is that `truth` was simply never declared. – Ben May 16 '18 at 12:06
  • 2
    @Ben they claim that it is a simplified version, and that it compiled and worked without the 'this'. So yes this code the op has presented wouldn't compile, but their question is most likely based on the question I have linked to. – matt May 16 '18 at 12:08
  • Yes, definitely true. I didn't see the (simplified). My bad. – Ben May 16 '18 at 12:10
  • Yep whoops in my simplification I accidentally declared the variable as truthParam rather than truth. sorry for the confusion – GregarityNow May 17 '18 at 13:05
  • But @matt how do you explain the console log of this to the console produced an instance of Hello rather than of Runnable? – GregarityNow May 17 '18 at 19:49
  • 1
    @Uzebeckatrente where did you put the 'this'. You either put it outside of the Runnable, and this referred to the class Hello. Or you put it inside of the Runnable, in which case it is an anonymous class inside of Hello, so when you print it out it will say `Hello$1@XXX`. That $1 (or $X where X is the number of anonymouse classes.) indicates it is an anonymous class inside of Hello. – matt May 18 '18 at 10:12

2 Answers2

4

First of all:

You are assigning this.truth = true. However you never declared this.truth.

You need to make a field private boolean truth if you want to have it as a field of your class or use final boolean truth = true in your constructor if you just want it locally (although that would be mostly pointless).


Regarding the usage of the this keyword:

this always refers to the object the method you are callign is residing in.

In your line if(this.truth) { you are in the method run of an object of type Runnable. So this refers to this exact object of type Runnable which does not have a field of name truth. Therefore this.truth throws an error.

You can either simply use truth or use Hello.this.truth.

The second would be mostly useful if you have a field truth in your class yourself but want to access the field in the class Hello.

So, imagine a simple use-case:

public class A
{
    private boolean field = false;

    public void foo()
    {
        Runnable r = new Runnable()
        {
            private boolean field = true;

            @Override
            public void run()
            {
                System.out.println(field); // prints true
                System.out.println(this.field); // prints true
                System.out.println(A.this.field); // prints false
            }
        };
    }
}
Ben
  • 1,665
  • 1
  • 11
  • 22
4

In your code, this refers to the object of the Runnable class. What you actually want is the object for the Hello class. If you're inside two classes, the this keyword will refer to the inner one. One way to refer to the outer one is writing Class.this In this case you need to change to (also a minor error in the name of the field)

public class Hello{

private Boolean truth;

public Hello(Boolean truthParam){

    this.truth = true;

    Runnable myRunnable = new Runnable(){
        public void run(){
            if (Hello.this.truth){
                System.out.println("i declare the truth!");
            }else{
                System.out.println("come back tomorrow");
            }

        }
   };
}

}

Wolforce
  • 79
  • 4