Просто запомните правило, что вызовы методов, разрешенные компилятором, основаны исключительно на объявленном типе ссылки, независимо от типа объекта.
Если это не очень понятно, вот еще одна версия этого правила: то, что слева, определяет методы, которые вы можете вызывать, независимо от того, что справа :)
Вот несколько примеров, чтобы прояснить ситуацию:
public interface Animal {
void voice();
}
public class Dog implements Animal {
public void voice() {
System.out.println("bark bark");
}
public void run() {
// impl
}
}
Когда вы создаете такую собаку:
Animal dog1 = new Dog();
Тип ссылки Animal
определяет, какие методы вам разрешено вызывать. Так что в основном вы можете звонить только:
dog1.voice();
Когда вы создаете такую собаку:
Dog dog2 = new Dog();
Тип ссылки Dog
, поэтому вам разрешено звонить:
dog2.voice();
dog2.run();
Это правило сохраняется и при наследовании классов, а не только при реализации интерфейса. Допустим, у нас есть что-то вроде:
public class SpecialDog extends Dog {
public void superPower() {}
}
А вот примеры того, что вы можете назвать:
Animal dog1 = new SpecialDog();
dog1.voice(); // only this
Dog dog2 = new SpecialDog();
// here you can call everything that Dog contains
dog2.voice();
dog2.run();
SpecialDog dog3 = new SpecialDog();
// here you can call all 3 methods
// this is the SpecialDog method
dog3.superPower();
// those 2 are inherited from Dog, so SpecialDog also has them
dog3.voice();
dog3.run();
В других случаях вам нужно использовать upcast / downcast, чтобы иметь возможность вызывать какой-то определенный метод.
Счастливого взлома :)