1. Метод C2.f(in i ) переопределяет I2.f(int i), реализует интерфейс
2. И одновременно C2.f(in i ) перегружает С2.f()
Это и переопределение и перегрузка одновременно, это нормально.
I2 o = new C2();
o.f(0);
В этом случае вызов второй строки - полиморфное поведение, вызов для ссылки типа I2 метода конкретного объекта типа C2.
А вызов переопределенного метода, с точки зрения Java - вызов другого метода. Поэтому кажется, что это переопределение.
Практически важно понимать:
1. Каким правилам кодирования следовать, если проектируешь полиморфное поведение и ожидаешь перегрузку, чтобы вместо неё случайно не поличить перегрузку. И наборот, если планируешь перегрузку, не получить переопределение.
2. Переопределение работает в иерархии наследования. Встречается случаи сочетания наследований от интерфейсов и других классов, которые могут дать совсем не тот результат, который ожидаешь. Могут привести к ошибке компиляции, если такое наследование нарушит правила. Эти случаи описаны в спецификации.
Именно случай с возникновением ошибки описан в книге - смотри комментарий к закоментированному коду внизу - если его раскомментировать, будет ошибка компиляции. Это практический смысл примера книги.
Название в книге правильное - перегрузка,
потому что соответствует спецификации - смотри jls-8.4.9. Overloading
https://docs.oracle.com/javase/specs/jls/se11/html/jls-8.html#jls-8.4.9Если два метода класса (объявлены ли оба в одном классе
или оба унаследованы классом, или один объявлен и один унаследован) имеют одно и то же имя, но сигнатуры, которые не являются эквивалентными для переопределения, тогда имя метода называется
перегружен .
Это тот редкий случай, когда перегрузка является полиморфизмом, хотя технически так же является и переопределением. Поэтому утверждение, что полиморфизм это только переопределение - формально не является верным.