Подтвердить что ты не робот

Есть ли способ гарантировать, что интерфейс расширяет класс в Java?

Предположим, что у меня есть следующая ситуация:

public abstract class Vehicle {
  public void turnOn() { ... }
}

public interface Flier {
  public void fly();
}

Есть ли способ гарантировать, что любой класс, реализующий Flier, должен также расширять Vehicle? Я не хочу, чтобы сделать Flier абстрактным классом, потому что я хочу иметь возможность смешивать несколько других интерфейсов аналогичным образом.

Например:

// I also want to guarantee any class that implements Car must also implement Vehicle
public interface Car {
  public void honk();
}

// I want the compiler to either give me an error saying
// MySpecialMachine must extend Vehicle, or implicitly make
// it a subclass of Vehicle. Either way, I want it to be
// impossible to implement Car or Flier without also being
// a subclass of Vehicle.
public class MySpecialMachine implements Car, Flier {
  public void honk() { ... }
  public void fly() { ... }
}
4b9b3361

Ответ 1

Интерфейсы Java не могут расширять классы, что имеет смысл, поскольку классы содержат сведения о реализации, которые не могут быть указаны в интерфейсе.

Правильный способ решения этой проблемы - полностью отделить интерфейс от реализации, превратив Vehicle в интерфейс. Car e.t.c. может расширить интерфейс Vehicle, чтобы заставить программиста реализовать соответствующие методы. Если вы хотите совместно использовать код среди всех экземпляров Vehicle, вы можете использовать (возможно, абстрактный) класс в качестве родителя для любых классов, которым необходимо реализовать этот интерфейс.

Ответ 2

Вы можете изменить классы и интерфейсы следующим образом:

public interface IVehicle {
  public void turnOn();
}

public abstract class Vehicle implements IVehicle {
  public void turnOn() { ... }
}

public interface Flier extends IVehicle {
  public void fly();
}

Таким образом, все реализации Flier гарантируют реализацию протокола транспортного средства, а именно IVehicle.

Ответ 3

Это странное требование, но вы можете выполнить что-то вроде Generics:

<T extends MyInterface & MyAbstractClass>

Ответ 4

Этот вопрос показывает, что вы не поняли суть interface и class. Забыв конкретный синтаксис Java прямо сейчас, все, что вам нужно понять первым, это то, что: interface - это набор протоколов, который должен быть несовместимым с реализацией. Нет смысла позволять интерфейсу расширять класс (который ориентирован на реализацию).

Вернемся к вашему конкретному вопросу, если вы хотите гарантировать, что a Flier всегда является своего рода Vehicle, просто измените последнее на interface и пусть он расширяет его (имеет смысл продлить один протокол из другого протокола). После этого вы можете создать любой класс (абстрактный или конкретный), который реализует Vehicle или Flier.

Ответ 5

Если у вас есть контроль над классами Vehicle, просто извлеките Vehicle в качестве интерфейса, а затем выполните базовую реализацию.

Если у вас нет контроля над классом Vehicle, например, потому что он является частью используемой структуры или библиотеки сторонних разработчиков, это невозможно сделать в Java.

Ближайшая вещь, которую вы можете сделать, это использовать нотацию универсальных подстановок Generics.

<T extends Vehicle & Car>

Но вы не можете применить его непосредственно к Car, если вы не сделаете что-то вроде этого:

public interface Car<T extends Vehicle & Car>() {
    T self();
}

Какой бот странный и не применяет метод self, чтобы фактически вернуть себя, это просто сильный намек/предложение.

Вы бы применили Car следующим образом:

public class CitroenC3 extends Vehicle implements Car<CitroenC3> {
    @Override
    public CitroenC3 self() {
        return this;
    }
}

можно использовать Car<?> следующим образом:

Car<?> car = obtainCarInSomeWay();
Vehicle v = car.self();
Car c = car.self();

они должны быть как действительным синтаксисом.

То, что компилятор обеспечивает здесь, - это то, что вы указали в Car<WHICH>, поскольку WHICH должен как расширить Vehicle, так и реализовать Car. И добавив self(), вы говорите программисту, что объект T должен быть самим объектом, тем самым заставляя экземпляр шаблона соответствовать классу, если он хочет быть совместимым со спецификацией.

в Java 8 вы даже можете определить реализацию по умолчанию для метода self.

Я также хотел бы, чтобы был лучший способ справиться с чем-то вроде этого.

Ответ 6

  • Определить новый пакет
  • Создайте новый интерфейс (т.е. HiddenOne) с областью действия "по умолчанию" с помощью метода "implementMe (HiddenOne)"
  • Переместите автомобиль и флаер в новый пакет.
  • Наследовать автомобиль и флаер из HiddenOne
  • Внедрить метод implementMe в Vehicle.

Теперь: Всякий раз, когда вы хотите реализовать из "Flier", вы должны перейти от Vehicle ! (потому что только Vehicle может реализовать implementMe).

Это сложно, но отлично работает.