protected data in abstract class

2019-02-08 13:47发布

问题:

My question involves specifically Java, abstract classes, and the use of protected data. I am being told that all the data should be private, and protected getters/setters used only.

Now, I understand we want to shield data from direct manipulation by casual users of the class, and that public data members in general are a questionable practice. I have looked at "Java protected fields vs public getters" ( Java protected fields vs public getters ), but I still am dubious that:

protected int i;  

is worse in an abstract class than:

private int i;  
protected int geti();  
protected void seti(int j); 

I am just not seeing the down side when the abstract class is there precisely to provide parent/common facility to the children classes, and the protected scope is meant to provide access to children, while protecting the data from casual users. I note in the question referenced above, that most of the answers seem to address the issue of why data in general should be private rather than public. I am trying to focus my question specifically on data existing in an abstract parent intended for use by the children. The sole reasonable comment I have heard to date is that using the parents protected data (e.g., int i above) leaves you with code in the child class that references a variable not declared in the child class. Less compelling is the argument (see Common protected data member in base class? ) that you may want to change the access some day, and now you have to honor your interface. This is an abstract class, and is intended to be extended 100% of the time.

Thanks! Specific Title/page# references to books are far more helpful that references to "..any basic Java programming text..."

========================================== 10-13-2010
This was as much a question about abstract classes as it is about protected data. I find it disappointing that the focus seems to have shifted in the responses to whether data hiding is a good thing in OOP (answer: yes). There's a lot of depth here involving the nature of the abstract class, and how it differs from a regular non-final class, and what possible advantages there might be for fixing the names and types of data-items in the abstract parent for use by the child classes. I think there is the possibility here for innovation and greater control being extended down from the abstract parent to the implementing child classes. I am concerned that general principles, such as the advantages of data-hiding, can become dogma, and inhibit innovation and the development of new patterns and ideas.

Thanks to all who contributed.

回答1:

If the field is private and access is through getters and setters, you will be able to reimplement getters and setters (for instance, dropping the field and updating/reading the value from an external source), and thus change how the "field" works without touching any child classes.

Whether this is worth it, that's up to you.



回答2:

Think of protected methods as an interface for subclasses, in the same way that public methods are an interface for everyone else.

Providing accessors enables the base class to maintain its state: there's no way a subclass would corrupt it without an intentional trick.



回答3:

Having less access isn't a drawback, it's a benefit. Classes should always limit access to as much of their internal state as possible. Don't think of why internals should be hidden, instead think of why they should be exposed. In this case as in every case, unless there is a really good reason to expose the variable then don't expose it.



回答4:

If you don't need your child to directly access it, why would you let them ?

It isn't a down side to use protected. But if it isn't necessary, maybe it's better to avoid it and control access on your fields.



回答5:

In Java protected members are accessible to all members in the same package in addition to any extending classes. Making the field private will prevent classes in the same package from directly accessing it.

As well there is the point that alex raised earlier.



回答6:

If someone subclasses your class, and puts the subclass in the same package as your current class, they may want to override your getters and setters. For example, they wantto make sure that i may only be set to a value greater than 1.

Other than that, it's really up to you. The convention is that there are getters and setters for everything though.



回答7:

Information hiding is valuable, even among classes related by inheritance.

In addition to allowing re-implementation, as noted by alex above:

  • You can set breakpoints in methods.
  • You can add constraints in one place.


回答8:

You want to use getters/setters because using protected int i; allows for field overriding (which you want to avoid at all costs).

You want to disallow field overriding because it works differently than method overriding. Field overriding does not make the overridden field inaccessible (the type of the reference determines which instance of the field you are working with).

Accessible fields should be final or in a class that is final.

public class OverridingFun {
    public static class Base {
        public int i = 1;
        public int getI(){ return i; }
    }
    public static class A extends Base {
        public int i = 2;
        public int getI(){ return i; }
    }
    public static class B extends A {
        public int i = 3;
        public int getI(){ return i; }
    }
    public static void main(String [] args){
        B b = new B();
        A bAsA = b;
        Base bAsBase = b;

        System.out.println(b.getI());//3
        System.out.println(bAsA.getI());//3
        System.out.println(bAsBase.getI());//3

        System.out.println(b.i);//3
        System.out.println(bAsA.i);//2
        System.out.println(bAsBase.i);//1

        b.i = 4;
        bAsA.i = 5;
        bAsBase.i = 6;

        System.out.println(b.i);//4
        System.out.println(bAsA.i);//5
        System.out.println(bAsBase.i);//6
    }
}

At first glance this looks like something that would just make code hard to read but it has implications on functionality. Say the field does get overridden by a derived class, since setters are not being used, there is no way to automagically update the base field and no way to detect if someone has changed the base field (since the base value is still accessible) and update the derived field. It's easy to imagine that the base and derived states could get out of sync and that the errors would be hard to track down. Simply put it makes for a very brittle API.

Unfortunately there is no way to guard against this since the final keyword, which protects against overriding, also makes fields write-once. So no writable non-overloadable fields.

Personally I'm rather surprised the language designers allowed field overriding at all. The advantage of using setters is that each level can guaranty the integrity of it's own state and trust that derived classes haven't fouled it up. Field overriding is just asking for trouble.