web-dev-qa-db-fra.com

Existe-t-il un moyen de garantir qu'une interface étend une classe en Java?

Supposons que j'ai la situation suivante:

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

public interface Flier {
  public void fly();
}

Existe-t-il un moyen de garantir que toute classe qui implémente Flier doit également étendre Vehicle? Je ne veux pas faire de Flier une classe abstraite parce que je veux pouvoir mélanger quelques autres interfaces de la même manière.

Par exemple:

// 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() { ... }
}
25
math4tots

Les interfaces Java ne peuvent pas étendre les classes, ce qui est logique car les classes contiennent des détails d'implémentation qui ne peuvent pas être spécifiés dans une interface.

La manière appropriée de résoudre ce problème consiste à séparer complètement l'interface de l'implémentation en transformant également Vehicle en interface. Le Car e.t.c. peut étendre l'interface Vehicle pour forcer le programmeur à implémenter les méthodes correspondantes. Si vous souhaitez partager du code entre toutes les instances Vehicle, vous pouvez utiliser une classe (éventuellement abstraite) comme parent pour toutes les classes qui ont besoin d'implémenter cette interface.

27
thkala

Vous pouvez réorganiser vos classes et interfaces comme ceci:

public interface IVehicle {
  public void turnOn();
}

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

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

De cette façon, toutes les implémentations de Flier sont garanties d'implémenter le protocole d'un véhicule, à savoir IVehicle.

9
Codo

C'est une exigence étrange, mais vous pouvez accomplir quelque chose du genre avec Generics:

<T extends MyInterface & MyAbstractClass>
2
user207421

Cette question montre que vous n'avez pas saisi l'essence de interface et class. Oublier la syntaxe concrète Java en ce moment, tout ce que vous devez d'abord comprendre, c'est que: l'interface est un ensemble de protocoles, qui devrait être indépendant de l'implémentation. Cela n'a aucun sens de laisser une interface étendre un (qui est orientée vers l'implémentation).

Revenons à votre question concrète, si vous voulez garantir qu'un Flier est toujours une sorte de Vehicle, changez simplement ce dernier en interface et laissez ancien l'étendre (Il est logique d'étendre un protocole à l'autre). Après cela, vous pouvez créer n'importe quelle classe (abstraite ou concrète) qui implémente Vehicle ou Flier.

1
Hui Zheng

Si vous avez le contrôle sur les classes Vehicle, extrayez simplement Vehicle en tant qu'interface, puis fournissez une implémentation de base.

Si vous n'avez aucun contrôle sur la classe Vehicle, par exemple parce qu'elle fait partie d'un framework que vous utilisez ou d'une bibliothèque tierce, ce n'est pas possible de le faire en Java.

La chose la plus proche que vous pouvez faire est d'utiliser la notation générique à caractères génériques multiples.

<T extends Vehicle & Car>

Mais vous ne pouvez pas vraiment l'appliquer directement à Car à moins que vous ne fassiez quelque chose comme ceci:

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

Ce qui est bizarre et n'impose pas la méthode self pour retourner réellement soi, c'est juste un indice/suggestion fort.

Vous implémenteriez un Car comme ceci:

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

on peut utiliser un Car<?> comme ceci:

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

ils doivent être tous les deux une syntaxe valide.

Ce que le compilateur applique ici, c'est que ce que vous spécifiez dans Car<WHICH> Comme QUI doit à la fois étendre Vehicle et implémenter Car. Et en ajoutant self() vous dites au programmeur que l'objet T est censé être l'objet lui-même, forçant ainsi l'instance générique à correspondre à la classe s'il veut se conformer à la spécification.

dans Java 8 vous pouvez même définir une implémentation par défaut pour la méthode self.

Je souhaite également qu'il y ait une meilleure façon de gérer quelque chose comme ça.

1
Daniele Segato
  1. Définir un nouveau package
  2. Créer une nouvelle interface (ie. HiddenOne) avec la portée "default" avec une méthode "implementMe (HiddenOne)"
  3. Déplacer le véhicule et le dépliant vers le nouveau package.
  4. Hériter d'un véhicule et d'un dépliant de HiddenOne
  5. Implémentez la méthode implementMe dans Vehicle.

Now: Chaque fois que vous souhaitez implémenter à partir de "Flier", vous devez étendre depuis Vehicle ! (car seul le véhicule peut implémenter implementMe).

C'est délicat mais fonctionne très bien.

0
Peter Rader