7

The question about access to protected member in Java was already asked and answered a lot of times, for example: Java: protected access across packages

But I can't understand why it is implemented this way, see explanation from "Java Programming Language" (4 ed.):

"The reasoning behind the restriction is this: Each subclass inherits the contract of the superclass and expands that contract in some way. Suppose that one subclass, as part of its expanded contract, places constraints on the values of protected members of the superclass. If a different subclass could access the protected members of objects of the first subclass then it could manipulate them in a way that would break the first subclass's contract and this should not be permissible."

OK, that's clear, but consider this inheritance structure (extract from some code):

package package1;

    public class A {
        protected int x;
    }

package package2;

public class B extends A {
    public static void main(String[] args)
        C subclass = new C();
        subclass.x = 7;  // here any constraints can be broken - ??
    }
}

class C extends B {
    // class which places constraints on the value of protected member x
    ...
}

Here subclass.x = 7 is a valid statement which still can break a C's contract. What am I missing?

Edited (added): Maybe I should not apply the cited logic in this situation? If we were dealing with only one package, the no restrictions exist at all. So maybe direct inheritance chain is treated in simplified way, meaning that superclass must know what it is doing...

Community
  • 1
  • 1
TT_ stands with Russia
  • 1,785
  • 3
  • 21
  • 33
  • 1
    Part of the issue here is that you've given class `B` knowledge of class `C`. That violates an assumption implicit in the reasoning you cited. – Ted Hopp Jun 23 '13 at 04:51
  • @TedHopp Not quite: awareness of the existence of a class is not the same as the awareness of its contract. Especially since the class contract may change while the name stays the same. – Marko Topolnik Jun 23 '13 at 08:05
  • @MarkoTopolnik - Class `B` (or, at least, `B.main`) knows that `C` has a member named `x`. Moreover, that member is accessible to `B.main` only because `main` is part of class `B`. That's more than "awareness of the existence" of class `C`. – Ted Hopp Jun 23 '13 at 08:26
  • @TedHopp But to know about `C#x`, B doesn't at all need to be aware of C. – Marko Topolnik Jun 23 '13 at 09:19
  • @MarkoTopolnik - To create an instance of `C` it does. And if you were to argue that `B` can be fed an instance of `C` and could change it's `x` value, all without knowing about `C`, then you would be changing the public contract of `B` in a way that would be inconsistent with `C`'s restriction on the value of `x`. – Ted Hopp Jun 23 '13 at 15:35
  • 1
    @tedHopp Quite true. My point does however invalidate what you have said earlier. You said part of the issue is that OP, by virtue of giving `B` the knowledge of `C`, has already violated the assumptions behind the reasoning. As we have seen, giving or not giving `B` the knowledge of `C` is immaterial with respect to the accessibility of `x`, so it can hardly be part of the issue. – Marko Topolnik Jun 23 '13 at 15:49
  • @MarkoTopolnik -The only reason there's a problem is because `main()` has both direct access to `x` (as a member of `B`) and also is creating an instance of `C`. Move `main()` to an entirely unrelated class (in a different package) and it won't compile. Alternatively, move `main()` to class `C` and then `C` is internally inconsistent with regards to the constraints it places on `x`. – Ted Hopp Jun 23 '13 at 18:05
  • @TedHopp Do you think the question is specific to the `main` method? I think it's about the class `B` violating the constraints of class `C`. The code is in `main` just for the sake of the example. I think OP is confusing the meaning of "constraints" in the quote, though. It is quite natural for the superclass to be in control of protected members; the subclass must be aware of what it does with them and work from that point onwards. – Marko Topolnik Jun 23 '13 at 18:17
  • The quote, however, is about having `B extends A` and `C extends A`, so `B` and `C` are siblings. In this case `B` cannot access `C#x`, and the reasoning why it can't is what the quote is all about. – Marko Topolnik Jun 23 '13 at 18:22
  • @Marko So your answer is that the quote is implicitly about the siblings only (they just say "one subclass" and "a different subclass"). Ok, it is probably so, I meant the same in the end of my Q (edited part). BTW, I don't think the meaning of "constraints" can be confused: constraints on the values - pretty much clear, constrained values form a proper subset of a set of all possible values for x in the superclass. – TT_ stands with Russia Jun 23 '13 at 23:37
  • Not necessarily siblings, but definitely on separate branches of the hierarchy. When you said that my "...Java allows you to do less on a more specific type..." is in opposition with the quote, it followed that you took the constraints as Java-imposed. – Marko Topolnik Jun 24 '13 at 07:30

3 Answers3

2

It's ultimately all about following contracts, as stated in your posted quote. If you're really worried that someone won't read the contract, then there's a defensive programming solution to all this that introduces validation on modification.

By this I mean that the code you posted can break contract; this, however, couldn't:

public class A {
    private int x;

    protected final void setX(int x) throws IllegalArgumentException {
        if (x < 0)
            throw new IllegalArgumentException("x cannot be negative");
        subValidateX(x);
        this.x = x;
    }

    /**
     * Subclasses that wish to provide extra validation should override this method
     */
    protected void subValidateX(int x) {
        // Intentional no-op
    }
}

Here, I've done three major things:

  1. I made x private so it can only be assigned from within A (excluding things like reflection, of course),
  2. I made the setter final which prevents subclasses from overriding it and removing my validation, and
  3. I made a protected method that can be overridden by subclasses to provide extra validation in addition to mine to make sure that subclasses can narrow requirements on x, but not widen them to include things like negative integers since my validation already checked that.

There are lots of good resources for how to design for inheritance in Java, especially when it comes to super-defensive protect-the-contract API programming like my example above. I'd recommend looking them up on your favorite search engine.

Ultimately, though, the developer writing the subclass needs to be responsible enough to read documentation, especially when you get into interface implementation.

Brian
  • 17,079
  • 6
  • 43
  • 66
  • Voted it up since it is a useful point. However, it does not answer the Q.Your last statement is correct, but in this case it is a superclass who breaks the contract of the subclass. – TT_ stands with Russia Jun 23 '13 at 16:54
0

Inherited classes are implicitly friends with their parent. So as soon as C is inherited from B, it is actually normal that B has the vision on C's x attribute.

Jerska
  • 11,722
  • 4
  • 35
  • 54
  • Yes, but can a superclass break a subclass' contract? – TT_ stands with Russia Jun 23 '13 at 04:40
  • 1
    Your example proves it. A friend relationship works in both ways. Something that would be interesting to test, though, would be to tru with a new attribute only defined in C. If the friend relationship is on the class, then it will work, else it's only on all attributes and methods. – Jerska Jun 23 '13 at 04:43
0

Since C extends B, having

C c = new C();
c.x = 1;

is, with respect to your issue, exactly the same as

B b = new C();
b.x = 1;

Java compiler doesn't consider the runtime type of the object referred to by b and c in the above code; all it sees is the declared type, which is B and C, respectively. Now, since my second example obviously must work (the code in class B is accessing its own property, after all), it follows that the first example must work as well; otherwise it would mean that Java allows you to do less on a more specific type, which is a paradox.

Marko Topolnik
  • 195,646
  • 29
  • 319
  • 436
  • "...Java allows you to do less on a more specific type..." - but this is exactly what is written in the quote I cited! (...subclass, as part of its expanded contract, places constraints on the values of protected members...) – TT_ stands with Russia Jun 23 '13 at 16:59
  • No, it has nothing to do with the quote; you may have misunderstood the text, then. The text is about the constraints on the level of program logic, not the constraints on the accessibility of class members. – Marko Topolnik Jun 23 '13 at 17:06