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
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.
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.
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.
Information hiding is valuable, even among classes related by inheritance.
In addition to allowing re-implementation, as noted by alex above:
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.
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.