Which is preferable in Java: storing a field twice, or storing it as protected in the base class?
I rarely use inheritance, but when I do I sometimes encounter the following problem. Given:
- I want to inject an object in the parent class constructor, say a
Logger
. - Given both the parent and the subclass need to use this field.
- Given I don't want to expose the field to the outside world (no getter).
I have seen this tackled in two different ways:
class ChildClass extends ParentClass {
private Logger logger;
public ChildClass(Logger logger) {
super(logger);
this.logger = logger;
}
}
class ParentClass {
private Logger logger;
public Pa开发者_开发问答rentClass(Logger logger) {
this.logger = logger;
}
}
Or:
class ChildClass extends ParentClasss {
public ChildClass(Logger logger) {
super(logger);
}
}
class ParentClass {
protected Logger logger;
public ParentClass(Logger logger) {
this.logger = logger;
}
}
I guess another option would be a protected getter in ParentClass
but I have never encountered this.
Personally, I prefer the second approach with the rationale that smaller data size trumps encapsulation, but I have often encountered the first version in the wild. Is there a stricly preferable way to do this?
I don't think there's a strictly preferable way, but in general, if you know the logger may be needed by subclasses - as you do in this case - then you should make it accessible, either by making the field protected or by adding a protected getter. Some people would say adding the getter is preferable.
One reason you see duplicated fields "in the wild" is because the subclass was written second, and modifying the parent class is not always possible.
The second option is better from a design and performance perspective, assuming that the logger is needed in the parent class. Having two attributes containing the same value is potentially confusing, and uses more space. (The latter is only significant if large numbers of instances are created.)
However:
- I'd declare it as
private
in the parent class, and provide aprotected final
getter method. - If you do declare it as
protected
attribute of the parent class, you should alsop make itfinal
. If you are using one of the standard logging APIs, it is normal practice to have the object instantiate its own logger; e.g. with
Log4j
this.logger = Logger.getLogger(this.getClass());
These are all relatively minor issues, but (IIRC) the default PMD ruleset includes a rule that whinges about protected
attributes.
... but I have often encountered the first version in the wild.
Yea well, lots of code in the wild is less than perfect, and as I opined, these are relatively minor issues.
FOLLOWUP
say both fields are final. In the first case, do you think the compiler would be clever enough to figure out the pointers will always be the same and collapse them into the same field?
No. I don't think that the compiler would do that:
Any optimization has to work in the face of reflective access (and even update) of the attributes. In this case, the reflection code would need to know about the optimization and map requests for one version of the field to the other.
In most cases the payoff for such an optimization would small, and the CPU cost of the JIT compiler figuring out that the optimization is valid would be significant.
(Bear in mind that this optimization can only be performed by the JIT compiler. The bytecode compiler doesn't have enough information to do the optimization. Specifically, it doesn't know what the final code of the class constructors is. Also bear in mind that unless there are large numbers of instances of the objects in question, the space saving would be insignificant.)
However, I've been using Guice for a while ant it looks like a standard practice to inject the logger instead of relying on a static factory.
I think that is a Guice-specific idiom / practice. Plain Java applications / libraries rarely pass loggers as arguments in my experience.
If you absolutely do not want to expose the field to the outside world, you would need to make it private
, as even a protected
field is visible to members of it's package (as well as any subclass). However, that might be overkill in the case of a Logger
. Inheritance might be an easier to understand/maintain implementation.
I would also use the second approach.
I'm not certain why you're so concerned about memory footprint, but the difference betweent the two approaches is negligible. Technically, you are not storing logger
twice. In Java, all object variables are pointers. So in the first scenario, both ParentClass
and ChildClass
are pointing to the same logger
instance.
The second approach is a classic example of one of the advantages of having inheritance, that is sharing functionality and properties.
To be honest, this is a design decision that is completely up to you, neither have a real significant impact on memory footprint, but the second approach utilizes the principles of OOP more than the first.
精彩评论