Есть ли способ гарантировать, что интерфейс расширяет класс в 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() { ... }
}
Ответы
Ответ 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).
Это сложно, но отлично работает.