Eric said:
[...]
Are you saying that class Bird ought not to be instantiable?
Yes.
There are no bird instances in the real world. They are all
instances of some type of bird.
That way lies madness. There are no Parrot instances in
the real world, only Polly and Snoofles and FeatheredBastard and
so on. Even Polly has no instances: There's Polly son of Polly
daughter of Polly son of Gawain, and PollyOfUnknownAntecedents,
and so on and so forth. Since the human mind is too small to
encompass all individual things in Nature or in imagination, it
is eventually necessary to generalize, to lump a whole aviary
of Pollys and Snoofleses and FeatheredBastards and so on into
Parrot. Or, let it be said, into Bird.
"Must every little attribute have a class all its own?"
I'm an amateur bird-watcher - very amateur - and I routinely classify
birds as Birds. I'm not totally helpless - if I spot crows or robins or
grackles or eagles I know what they are specifically - but most birds
for me are Birds. Usually only if they are ducks or geese can I tell you
exactly what they are.

For most of my mental models - and for quite
a few software models that I can imagine - Bird would suffice as a
template. A thing could genuinely and practically be an instance of Bird.
Problems like this is why I am less and less enthused with inheritance
and rigid class trees by the year. I've never actually used inheritance
a lot, and I use it less now than I ever did at the beginning of
learning OOP. My canonical example of inheritance frustration is
Vehicle: I've never come up with an inheritance tree starting with
Vehicle that doesn't leave a bad taste in my mouth. For a lot of my
mental models, and for quite a few software models that I can think of,
Vehicle is sufficient as a template. Objects could in fact be instances
of Vehicle.
Here's another example: Fastener. Go ahead and do up an inheritance tree
for Fastener that leaves you feeling warm and fuzzy.
I have very flat inheritance trees these days; always have had for the
most part. Aggregation/composition and interfaces go a lot further for
me than inheritance ever will. I don't mean to suggest that I make
minimal use of inheritance - I still use it a fair bit - but in recent
years almost all my use of it has been for implementation concerns like
re-using common code in JPA domain objects, _not_ for high-level design
per se.
Moving outside Java, and not focusing on any one other given language,
for me the object ideal is that of a fairly generic instance with
identity, that assumes characteristics and responsibilities as required,
and sheds them when it no longer needs them. If there were any inherent
"type" or classification, it might well be as high-level as Bird or
Vehicle or Human. A Human, at various points, might have the
characteristics and/or behaviours of a Student or Teacher or Employee or
Employer or Voter or Driver or DeceasedPerson or Felon, but those are
precisely characteristics and behaviours that the instance of Human
claims through composition/aggregation, and through implementation of
interfaces. There is no need to describe any of this through
inheritance, not in a high-level model/design sense, and I myself think
it's counter-productive.
I was going to extol some other languages in comparison to Java, but
then it occurred to me that while other languages might have features
that make some of these things easier to do, all of these concepts can
be implemented with no great difficulty in Java.
AHS