What is the scope of the local variable/methods of

2019-02-27 10:42发布

I am testing the snippet below, I need to know how can I access t.x or t.hello? What is its scope? Do developers define variables in this way?

public class Test{

public Test(){
    System.out.print("constructor\n");
}

public static void main(String[] args) {

    Test t = new Test(){
        int x = 0;
        //System.out.print("" + x);
        void hello(){
            System.out.print("inside hello\n");
        }
    };
}

Edit

But why this snippet worked

 Thread  tr = new Thread() {
int loops = 1;

 @Override
 public void run() {
    loops += 1;
}
};
tr.start();

标签: java scope
4条回答
我只想做你的唯一
2楼-- · 2019-02-27 10:55

You should distinguish declaration and definition.

In your case you declare a variable of class Test and assign it to an object of some class derived from Test (it's an anonymous class) which has some additional stuff in it.

The code after this definition sees t of class Test only, it knows nothing about x and hello because Test doesn't have them.

So, apart from reflection, you cannot use x and hello after definition of an anonymous class. And yes, developers use such variables when they need these variables inside of definition.

It was mentioned that you can call methods and access variables that are not part of Test immediately after definition:

int y = new Test(){
    int x = 0;
    //System.out.print("" + x);
    void hello(){
        System.out.print("inside hello\n");
    }
}.x;

This can be done because at this point the type of an object is know (it's the anonymous class). As soon as you assign this object to Test t, you lose this information.

查看更多
欢心
3楼-- · 2019-02-27 11:05

As written, neither x nor hello can be accessed outside the object referenced by t. The problem is that they are only declared in t's anonymous class. They are not defined in Test, and it is not possible to cast the t reference to the type in which they are declared, because it is anonymous.

I have modified Test to make it abstract and add an abstract declaration of hello, allowing it to be called from outside the object referenced by t. I have also modified hello to add a use of x. Those changes illustrate the two ways of accessing anonymous class features - though the base class or internally.

public abstract class Test {

  abstract void hello();

  public Test() {
    System.out.print("constructor\n");
  }

  public static void main(String[] args) {

    Test t = new Test() {
      int x = 0;

      // System.out.print("" + x);
      void hello() {
        System.out.print("inside hello\n");
        System.out.print("My value of x is "+x);
      }
    };
    t.hello();

  }
}
查看更多
我欲成王,谁敢阻挡
4楼-- · 2019-02-27 11:07

Your code creates an anonymous inner class. It's (more-or-less) equivalent to doing this:

public class Test {
    // ...
    private class TestAnonymousInner extends Test {
        int x = 0;
        //System.out.print("" + x);
        void hello(){
            System.out.print("inside hello\n");
        }
    }

    public static void main(String[] args) {
        Test t = new TestAnonymousInner();
        // invalid:
        // t.hello(); 
    }
}

If you look at the compiler output for that file, you'll notice a file named something like Test$1.class – that's the anonymous class you've defined.

Since the variable you're storing the instance in is of type Test, the fields aren't accessible through it. They're accessible through reflection, or from the constructor expression. E.g. the following works, although it's not particularly useful:

new Test() {
    void hello() {
        System.out.println("hello()");
    }
}.hello(); // should print "hello()"

Re: your edit. start() is a method of Thread. The variable tr is also of type Thread, so you can call its methods. Just not methods you add in an AIC.

查看更多
我命由我不由天
5楼-- · 2019-02-27 11:10

It creates an anonymous inner class. In this case it has little use. Anonymous classes are generally used to implement an interface without creating a whole new class. When you do that, you can add as many members as you want. For example:

Runnable r = new Runnable() {
    int i = 0;
    public void run() {
        System.out.println(i++);
    }
};

By doing that, you have added a counter to your implementation of the Runnable interface (which does not have such a field) and every time you call r.run(); an incremented value gets printed.

A less contrived example that uses a similar pattern:

private final ExecutorService executor = Executors.newCachedThreadPool(new ThreadFactory() {
    private final AtomicInteger threadId = new AtomicInteger();

    @Override
    public Thread newThread(Runnable r) {
        return new Thread(r, "Thread #" + threadId.incrementAndGet());
    }
});

Here, a basic implementation of ThreadFactory is provided which names each new Thread Thread #i.

查看更多
登录 后发表回答