Consider the case where your list is a function parameter, not a local variable:
public void eatFruit(List<? extends Fruit> list)
{
for (Fruit fruit : list)
{
eat(fruit);
}
}
You can pass a List<Banana> or a List<Apple> to this method, or even a List<Cherry>. The compiler knows only that the "?" in "? extends Fruit" represents some class that extends Fruit, but not what that class is. It has no way of knowing whether a Banana is an instance of that class, so it won't let you add one to the list. It will let you read the elements of the list and use them as fruit, as I did.
If you define the list as simply "List<Fruit> list", then it would allow you to add a Banana to the list, or any other type of fruit. But it would not allow you to call the function and pass in a List<Banana>, because a List<Banana> can only contain Bananas, but your function could add any type of fruit.
The whole point of Generics is that if you create a List<Cherry> you should never be able to add a Banana to it (unless you cast it to a raw List, defeating the purpose). The compiler knows that your list contains only fruit of a certain type (possibly any type), but it does not know what type that is. So it won't let you add anything to it. This is the only way to ensure that the list remains pure.