Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is access to protected member in Java implemented as it does?

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...

like image 690
TT_ Avatar asked Jun 23 '13 04:06

TT_


1 Answers

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.

like image 155
Brian Avatar answered Nov 15 '22 00:11

Brian