Exposition:
Suppose I have this trivial interface:
interface Y { Y f(); }
I can implement it in 3 different ways:
Use the general type everywhere.
class SubY_generalist implements Y { public Y f() { Y y = new SubY_generalist(); ... return y; } }Use the special type, but return the same value implicitly cast into general type.
class SubY_mix implements Y { public Y f() { SubY_mix y = new SubY_mix(); ... return y; } }Use the special type and return it the same.
class SubY_specialist implements Y { public SubY_specialist f() { SubY_specialist y = new SubY_specialist(); ... return y; } }
My considerations:
There is a lengthy conversation nearby here on the benefits of "programming to an interface". The most promoted answers do not seem to go deep into the distinction between argument and return types, which are in fact fundamentally distinct. Finding therefore that the discussion elsewhere does not provide me with a clean-cut answer, I have no choice but to speculate on it by myself — unless the kind reader can lend me a hand, of course.
I will assume the following basic facts about Java: (Are they correct?)
- An object is created at its most special.
- It may be implicitly cast into a more general type (generalized) at any time.
- When an object is generalized, it loses some of its useful properties, but gains none.
From these simple points, it follows that a special object is more useful, but also more dangerous than the general.
As an example, I may have a mutable container that I can generalize into being immutable. If I ensure the container has some useful properties before being thus frozen, I can generalize it at the right time to prevent the user from accidentally breaking the invariant. But is this the right way? There is another way to achieve similar isolation: I may always just make my methods package-private. Generalizing seems to be more flexible but easy to omiss and introduce a surface for subtle bugs.
But in some languages, like Python, it is deemed unnecessary to ever actually protect methods from outside access; they just label the internal methods with an underscore. A proficient user may access the internal methods to achieve some gain, provided that they know all the intricacies.
Another consequence is that inside the method definitions I should prefer specialized objects.
My questions:
- Is this right thinking?
- Am I missing something?
- How does this relate to the talk of programming to an interface? Some locals here seem to think it is relevant, and I agree that it does, just not immediately, as I see it. It is more like programming against an interface here, or against a subclass in general. I am a bit at a loss about these intricacies.
