web-dev-qa-db-fra.com

Pourquoi les méthodes statiques de classe sont-elles héritées mais ne sont pas interfacées?

Je comprends que dans Java, les méthodes statiques sont héritées de la même manière que les méthodes d’instance, à la différence que lorsqu’elles sont redéclarées, les implémentations parent sont masquées plutôt que remplacées. Bien, cela a du sens. Cependant, le tutoriel Java note que 

Les méthodes statiques dans les interfaces ne sont jamais héritées. 

Pourquoi? Quelle est la différence entre les méthodes statiques standard et d'interface?

Permettez-moi de clarifier ce que je veux dire quand je dis que les méthodes statiques peuvent être héritées:

class Animal {
    public static void identify() {
        System.out.println("This is an animal");
    }
}
class Cat extends Animal {}

public static void main(String[] args) {
    Animal.identify();
    Cat.identify(); // This compiles, even though it is not redefined in Cat.
}

Cependant,

interface Animal {
    public static void identify() {
        System.out.println("This is an animal");
    }
}
class Cat implements Animal {}

public static void main(String[] args) {
    Animal.identify();
    Cat.identify(); // This does not compile, because interface static methods do not inherit. (Why?)
}
42
Radon Rosborough

Voici ma supposition. 

Étant donné que Cat ne peut étendre qu'une classe si Cat extend Animal, alors Cat.identify n'a qu'une seule signification. Cat peut implémenter plusieurs interfaces pouvant chacune avoir une implémentation statique. Par conséquent, le compilateur ne sait pas lequel choisir?

Cependant, comme l'a souligné l'auteur, 

Java a déjà ce problème avec les méthodes par défaut. Si deux interfaces déclarer par défaut void identifier (), lequel est utilisé? C'est une compilation erreur, et vous devez implémenter une méthode de substitution (qui pourrait être juste être Animal.super.identify ()). Donc, Java résout déjà ce problème problème pour les méthodes par défaut - pourquoi pas pour les méthodes statiques?

Si je devais encore deviner, je dirais qu'avec default, la mise en œuvre fait partie de la variable Cat. Avec static cela ne peut pas être. La fonction principale doit être liée à quelque chose. Lors de la compilation, Cat.identify pourrait être remplacé par Animal.identify par le compilateur, mais le code ne correspondrait pas à la réalité si Cat était recompilé, mais pas la classe qui contient main.

17
agbinfo

Avant Java 8, vous ne pouviez pas définir de méthodes static dans une interface. Ceci est très discuté dans cette question . Je vais faire référence à cette answer (par utilisateur @ JamesA.Rosen) pour expliquer pourquoi les concepteurs Java ne souhaitaient probablement pas de méthodes static dans une interface initialement:

Il y a quelques problèmes en jeu ici. Le premier est la question de déclarer une méthode statique sans la définir. C'est la différence entre

public interface Foo {
  public static int bar();
}

et

public interface Foo {
  public static int bar() {
    ...
  }
}

Java ne permet pas non plus, mais cela pourrait permettre la seconde. Le premier est impossible pour les raisons mentionnées par Espo: vous ne savez pas lequel la classe d'implémentation est la définition correcte.

Java pourrait autoriser ce dernier, à condition de traiter les interfaces comme Objets de première classe. Les modules de Ruby, qui sont approximativement équivalent aux interfaces de Java, permettent exactement cela:

module Foo
  def self.bar
    ...
  end
end

Toutefois, depuis la publication de Java 8, vous pouvez réellement ajouter les méthodes default et static à l’intérieur de interface.

Je vais citer cette source beaucoup ici. C'est le problème initial:

La fonctionnalité de langage d'interface de Java vous permet de déclarer des interfaces avec méthodes abstraites et fournir des implémentations de ces méthodes dans le classes qui implémentent les interfaces. Vous devez mettre en œuvre chaque méthode, ce qui est fastidieux quand il existe plusieurs méthodes pour mettre en place. En outre, après la publication de l'interface, vous ne pouvez pas ajouter de nouveau fichier méthodes abstraites à elle sans casser la source et binaire compatibilité.

C'était la solution Java 8 fournissait default:

Java 8 résout ces problèmes en faisant évoluer l'interface pour prendre en charge méthodes par défaut et statiques. Une méthode par défaut est une méthode d'instance défini dans une interface dont l'en-tête de méthode commence par la valeur par défaut mot-clé; il fournit également un corps de code. Chaque classe qui implémente le interface hérite des méthodes par défaut de l'interface et peut remplacer leur

Et pour static:

Une méthode statique est une méthode associée à la classe dans laquelle c'est défini, plutôt qu'avec n'importe quel objet créé à partir de cette classe . Chaque instance de la classe partage les méthodes statiques de la classe . Java 8 permet également de définir des méthodes statiques dans des interfaces où elles peut aider les méthodes par défaut.

Lorsque vous implémentez une interface contenant une méthode statique, le fichier La méthode statique fait toujours partie de l'interface et non du classe d'implémentation. Pour cette raison, vous ne pouvez pas préfixer la méthode avec le nom de la classe. Au lieu de cela, vous devez préfixer la méthode avec l'interface prénom

Exemple:

interface X
{
   static void foo()
   {
      System.out.println("foo");
   }
}

class Y implements X
{
}

public class Z 
{
   public static void main(String[] args)
   {
      X.foo();
      // Y.foo(); // won't compile
   }
}

Expression Y.foo() ne sera pas compilé car foo() est un membre statique de l'interface X et pas un membre statique de la classe Y.

Les méthodes statiques dans les interfaces pourraient créer un diamant de mort si elles étaient héritées. Ainsi, appeler une méthode statique à partir de l'interface appropriée est suffisamment bon comparé au risque de l'appeler depuis une classe concrète pouvant implémenter plusieurs interfaces contenant des méthodes statiques du même nom.

Pourquoi les méthodes statiques sont-elles différentes?

Les méthodes statiques ne sont que des fonctions non liées aux objets. Au lieu de les placer dans des classes abstraites utilitaires (telles que l'appel de Collections.sort ()), nous déplaçons ces fonctions (méthodes statiques) vers leurs interfaces appropriées. Ils pourraient être liés aux objets hérités, comme le font les méthodes par défaut, mais ce n'est pas leur travail. Les méthodes statiques fournissent des fonctionnalités qui ne sont pas liées aux instances de la classe.

Exemple:

interface Floatable {

    default void float() {
        // implementation
    }

    static boolean checkIfItCanFloat(Object fl) {
         // some physics here
    } 
}

class Duck implements Floatable { }

Donc, le fait est qu'un canard peut flotter mais que la fonction qui vérifie si un objet flotte vraiment n'est pas quelque chose qu'un canard peut faire. C'est une fonctionnalité non pertinente que nous pourrions transmettre à notre interface Floatable au lieu de la placer dans une classe d'utilitaires.

3
Styl

Commençons par quelques antécédents ...

Java ne prend pas en charge l'héritage multiple (possibilité d'étendre plusieurs classes). En effet, l'héritage multiple est sujet au diamant mortel de la mort (également appelé problème du diamant) que les concepteurs de Java ont choisi de préempter. 

 enter image description here

Si B et C remplacent une méthode héritée de A, de quelle méthode D hérite-t-elle?

Une classe peut implémenter plusieurs interfaces _ parce que les méthodes d'interface sont contractées pour la substitution; si une classe C implémente deux interfaces A et B déclarant la même méthode, la même méthode en C sera invoquée par les clients de l'une ou l'autre des interfaces (A ou B). L'introduction de méthodes par défaut pour les interfaces dans Java 8 a été rendue possible en obligeant l'implémenteur à remplacer la valeur par défaut en cas d'ambiguïté. Ce compromis était acceptable car les méthodes par défaut sont censées être défensives (à utiliser si aucune autre implémentation n'est explicitement fournie par un implémenteur). Cependant, comme le compilateur ne peut pas vous forcer à écraser une méthode statique (les méthodes statiques ne peuvent pas en principe être écrasées), l'introduction de méthodes statiques pour les interfaces en Java est venue avec une restriction: les méthodes statiques d'une interface ne sont pas hérité.

0