В полиморфизме Java все классы, реализующие интерфейс, могут быть созданы как интерфейс этого типа?

Проходя мой онлайн-учебник, я наткнулся на этот урок. У меня есть интерфейс и два класса, которые реализуют этот интерфейс.

public interface Payable {
double getPaymentAmount();
}

и

  • класс Invoice, который реализует вышеуказанный интерфейс Payable
  • класс SalariedEmployee, который расширяет абстрактный класс Employee, реализующий интерфейс Payable.
  • Тестовый класс, содержащий основной метод для проверки этого.

Теперь в тестовом классе при создании массива объектов тип объекта назывался Payable[], а не SalariedEmployee[] или Invoice[], например

    public static void main(String[] args) {
    Payable[] payableObjects = new Payable[4];
    payableObjects[0] = new Invoice("0000", "abc", 1,2);
    payableObjects[1] = new SalariedEmployee("test", "user", "000-000", 35);
  • Это потому, что все классы реализуют интерфейс Payable[]?
  • Если интерфейс определен на верхнем уровне иерархии, всегда ли возможно создать объекты всех классов, реализующих этот интерфейс?

person scott    schedule 19.11.2015    source источник
comment
1. да, если бы это было не так, вы бы получили ошибку компилятора. 2. да, возьмите в качестве примера List или Map, где вы всегда должны объявлять переменную как List или Map, чтобы иметь возможность изменить присвоенный тип класса. Ваш общий вариант использования здесь заключается в том, что вы хотите вызывать только методы, предоставляемые этим конкретным интерфейсом.   -  person SomeJavaGuy    schedule 19.11.2015
comment
Спасибо @KevinEsche. Пожалуйста, опубликуйте это как ответ, и я приму его.   -  person scott    schedule 19.11.2015
comment
Да, в этом весь смысл полиморфизма.   -  person Peter Lawrey    schedule 19.11.2015


Ответы (2)


на ваш первый вопрос: да, вы бы получили ошибку компилятора, если бы это было не так.

во втором случае возьмите List или Map в качестве примера. Посмотрите на следующий пример. Мы объявляем список, но в зависимости от флага мы хотим, чтобы этот конкретный List действовал иначе, поскольку он представляет другой класс.

public class BookList{
    List <String> list;

    public BookList(boolean flag) {
        if(flag) {
            list = new ArrayList<>();
        } else {
            list = new LinkedList<>();
        }
    }
}

Поскольку мы объявили его просто как List, мы можем назначать различные типы списков, которые реализуют это interface. Вы можете очень просто изменить вариант использования этого класса, при этом вы по-прежнему можете получить доступ ко всем методам, предоставляемым интерфейсом.

Это то, что делает ваш массив Payable. Вы хотите назначить различные типы классов в этот массив, все из которых реализуют этот интерфейс.

Это упростило бы создание методов для этого конкретного интерфейса. Возьмите метод суммирования в качестве примера для вашего случая.

public int sumPayable(Payable[] payables) {
    int sum = 0;
    for(Payable p : payables) {
        sum += p.getPaymentAmount();
    }
    return sum;
}

В этом случае не имеет значения, какой фактический класс каждого из этих классов реализует Payable, поскольку вы можете просто передать массив, как вы создали, этому методу.

person SomeJavaGuy    schedule 19.11.2015
comment
Спасибо @Кевин. Я еще не встречал этого оператора <String>. Для чего он используется и как он называется? Если бы вы могли дать мне термин для поиска, я бы поискал его в Google. - person scott; 19.11.2015
comment
@scott это Generics - person SomeJavaGuy; 19.11.2015

Название вашего вопроса не является грамматическим, но выбор слова предполагает, что происходит тонкое непонимание концепций.

Вы можете создать класс только как этот класс. То есть вы не можете написать new Payable и каким-то образом ожидать, что будут созданы какие-либо объекты Invoice или SalariedEmployee.

Но вы можете сделать ссылку на точку Payable на любой объект, который реализует Payable. Это почти основная идея полиморфизма подтипов.

В примере происходит то, что создается массив из четырех ссылок Payable. Эти ссылки еще ни на что не указывают (они нулевые), и вокруг еще нет объектов Invoice или SalariedEmployee, только объект массива.

Затем код создает два объекта и назначает их двум ссылкам. Это работает так же, как если бы элементы массива были независимыми переменными. (Почти, но разница возникает из-за ковариантности массива, которая на данный момент не важна.) В частности, результат оператора new в первом случае имеет тип Invoice, а не Payable. Вы можете присвоить Invoice ссылке Payable, но преобразование происходит при назначении, а не при создании.

Таким образом, технически ответ на ваш вопрос в заголовке: «Нет, объекты всегда создаются как тип, указанный вами в выражении new, но впоследствии вы можете преобразовать ссылки».

person Sebastian Redl    schedule 19.11.2015