Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Access field of outer base class

Tags:

java

In Java, inner classes can normally access outer classes' private members. While writing an Android app, I had a static inner class which extended it's outer class. Turns out, private fields of the outer class could not be accessed:

class Outer {
  private int m_field = 1;

  static class Inner extends Outer {
    Inner() {
      m_field = 2;
    }
  }
}

It gives a confusing error message:

error: non-static variable m_field cannot be referenced from a static context

Even though nothing is static except the class itself.

When the field m_field is made protected, it compiles without a problem. But also, when doing this:

class Outer {
  private int m_field = 1;

  static class Inner extends Outer {
    Inner() {
      ((Outer)this).m_field = 2;
    }
  }
}

It works without problems. Is this a bug in the compiler? Why would you need to cast to the outer class, which you are already an instance of?


EDIT:

For a real use-case of this, consider a class like this:

public abstract class MyItem {
  private int m_counter = 0;
  public abstract int updateSomething();

  public static class CountItem extends MyItem {
    public int updateSomething() { m_counter++; }
  }

  public static class DoubleCountItem extends MyItem {
    public int updateSomething() { m_counter += 2; }
  }
}

Pretty abstract example, but it can be used to provide basic implementations for abstract classes which don't need a lot of code on their own.

EDIT2:

As @Nathan suggests, it seems like this problem can be recreated by 2 classes without nesting:

class Base {
  private int x = 0;

  void a(Extended b) {
    ((Base)b).x = 1; //<-- with cast: compiles, without: error
  }
}

class Extended extends Base {

}

Which gives better error message:

error: x has private access in Base

like image 801
Chrisu Avatar asked Aug 12 '14 18:08

Chrisu


1 Answers

What you are seeing here is that, as long as you're within the class definition of Outer, you have access to private members of anything that has the class Outer, including things that are cast to Outer. They have to have the same class (as opposed to being instances of the class, with a different concrete subclass).

The inner class thing is a complication, here's a smaller example:

public class A {
    private int foo = 0;

    public String toString() {
        return "A: foo=" + foo;
    }

    public static void main(String[] args) {
        B b = new B();
        System.out.println(b);
        ((A)b).foo = 1;
        System.out.println(b);
    }
}

class B extends A {

}

This compiles, only because it's within the class definition of A. Move the main method somewhere else (to within B, for instance), and you can no longer reference foo.

This is something that you see when coding equals methods, where you can access private fields of another instance of the same class because you're writing a method that's part of the class definition.

The Java language specification, in 6.6.1 Determining Accessibility:

Otherwise, if the member or constructor is declared private, then access is permitted if and only if it occurs within the body of the top level class (§7.6) that encloses the declaration of the member or constructor.

Without the cast to Outer access is not permitted because a) m_field is a private member of Outer, so it's not visible to subclasses, and b) it's not a member of the class being declared. Adding the cast means the compiler treats it as an Outer, and m_field becomes accessible.

like image 151
Nathan Hughes Avatar answered Oct 04 '22 05:10

Nathan Hughes