Right, if you take type-safety first, you shouldn't be able to. But a list of abstract fruits has very limited usage without the ability to accessing concrete fruit instance. The adoption of downcasting in some OO languages came out from such needs, given that they lacked generic and/or algebraic types. And with that regard, I thought your solution didn't address the original covariance/contravariance problem (that is, want to have heterogeneous list of fruits and allowing to access concrete types of individual elements).
There are type-safe ways, like making a fruit a sum type of apple and banana, or using traits or type classes, etc. But is-a/has-a discussion seems a bit off from that.
For this context, what I'm saying is that if you don't need to extract an apple from a list of fruits, then you can do it safely with inheritance, so whether using inheritance or delegation is irrelevant. Isn't it?
If you want to extract an apple from a list of fruits, you need runtime type information (so that you can test whether a particular fruit object happens to be an apple), which has nothing to do with whether your language has inheritance or not. Using runtime type information is a symptom of bad design, though, since it lessens the extent to which you can reason about programs (as pieces of text) by just looking at their types.
There are type-safe ways, like making a fruit a sum type of apple and banana, or using traits or type classes, etc. But is-a/has-a discussion seems a bit off from that.