33

I have an abstract class, relation in package database.relation and a subclass of it, Join, in package database.operations. relation has a protected member named mStructure.

In Join:

public Join(final Relation relLeft, final Relation relRight) {
        super();
        mRelLeft = relLeft;
        mRelRight = relRight;
        mStructure = new LinkedList<Header>();
        this.copyStructure(mRelLeft.mStructure);

        for (final Header header :mRelRight.mStructure) {
        if (!mStructure.contains(header)) {
            mStructure.add(header);
        }
    }
}

On lines

this.copyStructure(mRelLeft.mStructure);

and

for (final Header header : mRelRight.mStructure) {

I get the following error:

The field Relation.mStructure is not visible

If I put both classes in the same package, this works perfectly. Can anyone explain this issue?

Amir Rachum
  • 76,817
  • 74
  • 166
  • 248

4 Answers4

28

It works, but only you the children tries to access it own variable, not variable of other instance ( even if it belongs to the same inheritance tree ).

See this sample code to understand it better:

//in Parent.java
package parentpackage;
public class Parent {
    protected String parentVariable = "whatever";// define protected variable
}

// in Children.java
package childenpackage;
import parentpackage.Parent;

class Children extends Parent {
    Children(Parent withParent ){
        System.out.println( this.parentVariable );// works well.
        //System.out.print(withParent.parentVariable);// doesn't work
    } 
}

If we try to compile using the withParent.parentVariable we've got:

Children.java:8: parentVariable has protected access in parentpackage.Parent
    System.out.print(withParent.parentVariable);

It is accessible, but only to its own variable.

OscarRyz
  • 196,001
  • 113
  • 385
  • 569
  • Will an appropriate solution be to define a protected accessor? – Amir Rachum Jun 18 '10 at 19:58
  • 1
    Same thing would happen. More interesting would be to know if `Join` IS-A `Relation` and if so, why should they go in different packages. Probably, using an intermediate object to abstract the structure and would do. I would suggest you to move them in the same package for now, just to avoid enter in a coding paralysis. – OscarRyz Jun 18 '10 at 20:38
  • I believe your first sentence would be a little more accurate if you say that within the child class you can access that member on any instance of the child class, *or any instance of a class that inherits from the child class*, but not any instance of a class that the child class inherits from. (See section 6.6.2.1 of the specification: http://docs.oracle.com/javase/specs/jls/se7/html/jls-6.html#jls-6.6.2). So it can see it in further-derived members of the inheritance tree, just not in less-derived members. – Tim Goodman Mar 20 '14 at 01:44
  • 1
    Admittedly, that's kind of a mouthful to say. – Tim Goodman Mar 20 '14 at 01:48
  • @TimGoodman I don't see anything in 6.6.2.1 that makes this distinction. The only thing that comes close is the use of the word 'object' rather than 'class' in the single paragraph under [#6.6.2](http://docs.oracle.com/javase/specs/jls/se7/html/jls-6.html#jls-6.6.2) itself. – user207421 Jul 08 '14 at 09:45
  • @EJP Section 6.6.2.1 addresses access by a qualified name, e.g. `Q.Id`, and says "access is permitted [within `S`] if and only if the type of the expression `Q` is `S` or a subclass of `S`." So clearly one instance can access a protected member on a different instance, so long as it's of the right type. – Tim Goodman Jul 08 '14 at 16:44
  • @EJP In the OP's case, the access `mRelLeft.mStructure` fails not because he's accessing it from a different instance than `mRelLeft`, but rather because the compile-time type of `mRelLeft` (`Relation`) is less derived than the class he's accessing it from (`Join`). If `mRelLeft` had a compile-time type of `Join`, or a compile-time type that was more derived than `Join`, the access would succeed. (But as Levar Burton would say, "Don't take my word for it", it's easy enough to try it and see.) – Tim Goodman Jul 08 '14 at 16:45
  • It seem to not work from different packages. From different project(library). – Jacob Jan 09 '15 at 13:02
  • That was tricky to understand! One can access it from the class that extends it, but not by creating an object of ref superclass and tries to get it. I found it really tricky – Ced Jun 14 '16 at 08:14
14

A little known caveat about protected:

6.6.2 Details on protected Access

A protected member or constructor of an object may be accessed from outside the package in which it is declared only by code that is responsible for the implementation of that object.

OscarRyz
  • 196,001
  • 113
  • 385
  • 569
Marcus Adams
  • 53,009
  • 9
  • 91
  • 143
2

If protected, your instance of Join cannot access the mStructure in other instances (relRight, relLeft) outside the package.

EDIT:

The table here explains this situation fairly well. I marked the culprit in your question with []s

Access Levels
Modifier    Class Package Subclass  World
public      Y     Y       Y         Y
protected   Y    [Y]      Y         N
no modifier Y     Y       N         N
private     Y     N       N         N
Lauri Lehtinen
  • 10,647
  • 2
  • 31
  • 29
  • Mmmhh your first explanation only says, the the OP ask, in fist place , *"can't access it"*. Your edit, doesn't quite clarify the problem. – OscarRyz Jun 18 '10 at 17:43
  • I think my 1st explanation says pretty much the same thing as yours. – Lauri Lehtinen Jun 18 '10 at 17:46
  • And regarding the edit, how does the table not clarify the problem? The OP described the very behavior that one would expect based on the table - an instance of a class can access the protected members of another instance of any class, if they reside in the same package. Furthermore, an instance of a class can access the protected members defined in its parent class, even if the parent class is in another package. – Lauri Lehtinen Jun 18 '10 at 17:51
  • The text accompanying the table you quote specifically says what the OP is doing is allowed - "The protected modifier specifies that the member can only be accessed within its own package (as with package-private) and, in addition, by a subclass of its class in another package. " – Peter Recore Jun 18 '10 at 17:54
  • Maybe I'm blissfully reading it wrong then. I guess it comes down to what's considered a "Subclass" in this context. – Lauri Lehtinen Jun 18 '10 at 18:10
0

The problem is that you are accessing other instance protected member.

You can apply multiple solutions, for example if possible you can declare in the parent class these two methods:

protected void copyRelationStructure(Relation r) {
  this.copyStructure(r.mStructure);
}

protected void mergeRelationStructure(Relation r) {
  for (final Header header: r.mStructure) {
    if (!mStructure.contains(header)) {
      mStructure.add(header);
    }
  }
}

And then in childs code replace:

this.copyStructure(mRelLeft.mStructure);

for (final Header header :mRelRight.mStructure) {
  if (!mStructure.contains(header)) {
    mStructure.add(header);
  }
}

With:

this.copyRelationStructure(mRelLeft);
this.mergeRelationStructure(mRelRight);

That should work. Now Relation has the responsibility to provide methods that allow operations with itself inners to its children. Probably the reason behind this policy is that children should not mess with parent's internals unless they are part of the same software bundle in order to limit incompatibilities.

user1039663
  • 1,230
  • 1
  • 9
  • 15