web-dev-qa-db-fra.com

Quelles sont les différences entre les classes abstraites et les interfaces dans Java 8?

Dans Java il y avait autrefois une différence subtile mais importante entre les classes abstraites et les interfaces: implémentations par défaut . Les classes abstraites pouvaient en avoir, les interfaces non. Java 8 introduit cependant des implémentations par défaut pour les interfaces, ce qui signifie que ce n'est plus la différence critique entre une interface et une classe abstraite.

Donc qu'est-ce?

Autant que je sache, la seule différence qui subsiste (à part peut-être certaines sous l'efficacité du capot) est que les classes abstraites suivent le traditionnel Java héritage unique, tandis que les interfaces peuvent avoir l'héritage multiple (ou si vous le souhaitez). Cela m'amène à une autre question -

Comment les nouvelles interfaces Java 8 évitent le Diamond Diamond ?

64
David Grinberg

Les interfaces ne peuvent pas être associées à un état.

Les classes abstraites peuvent être associées à un état.

De plus, les méthodes par défaut dans les interfaces n'ont pas besoin d'être implémentées. Ainsi, de cette façon, il ne cassera pas le code déjà existant, car alors que l'interface reçoit une mise à jour, la classe d'implémentation n'a pas besoin de l'implémenter.
Par conséquent, vous pouvez obtenir du code sous-optimal, mais si vous voulez avoir un code plus optimal, votre travail consiste à remplacer l'implémentation par défaut.

Et enfin, en cas de problème de diamant, le compilateur vous avertira et vous devra choisir l'interface que vous souhaitez implémenter.

Pour en savoir plus sur le problème des diamants, considérez le code suivant:

interface A {
    void method();
}

interface B extends A {
    @Override
    default void method() {
        System.out.println("B");
    }
}

interface C extends A { 
    @Override
    default void method() {
        System.out.println("C");
    }
}

interface D extends B, C {

}

Ici, je reçois l'erreur du compilateur sur interface D extends B, C, Que:

interface D inherits unrelated defaults for method() form types B and C

La solution est:

interface D extends B, C {
    @Override
    default void method() {
        B.super.method();
    }
}

Dans le cas où je voulais hériter la method() de B.
Il en va de même si D était un class.

Pour en savoir plus sur la différence entre les interfaces et les classes abstraites dans Java 8, considérez les Team suivants:

interface Player {

}

interface Team {
    void addPlayer(Player player);
}

Vous pouvez en théorie fournir une implémentation par défaut de addPlayer telle que vous pouvez ajouter des joueurs par exemple à une liste de joueurs.
Mais attendez...?
Comment stocker la liste des joueurs?
La réponse est que vous ne pouvez pas le faire dans une interface, même si vous disposez d'implémentations par défaut.

68
skiwi

Il y a eu des réponses très détaillées, mais il semble manquer un point que je considère au moins comme l'une des très rares justifications à avoir des classes abstraites du tout :

Les classes abstraites peuvent avoir protégé membres (et membres avec visibilité par défaut). Les méthodes dans les interfaces sont implicitement public.

17
Marco13

La définition du problème du diamant est vague. Il existe toutes sortes de problèmes qui peuvent survenir avec l'héritage multiple. Heureusement, la plupart d'entre eux peuvent être facilement détectés au moment de la compilation, et les langages de programmation prennent en charge des solutions simples pour contourner ces problèmes. La plupart de ces problèmes ne sont même pas spécifiques au problème de diamant. Par exemple, des définitions contradictoires de méthodes peuvent également se produire sans losanges:

interface Bar {
    default int test() { return 42; }
}
interface Baz {
    default int test() { return 6 * 9; }
}
class Foo implements Bar, Baz { }

Le problème spécifique avec diamants est la question de inclus vs exclusif. Si vous avez une hiérarchie de types où B et C dérivent de A , et D dérive de B et C , alors la question est:

  • est D a B * et * a C (c'est-à-dire un type de A ), ou
  • est D a B * ou * a C (c'est-à-dire deux types de A ).

Eh bien, en Java 8 le type A doit être un interface. Donc ça fait aucune différence, car interfaces n'ont pas d'état. Peu importe que interfaces puisse définir méthodes par défaut, car elles n'ont pas non plus d'état. Ils peuvent invoquer des méthodes qui ont un accès direct à l'état, mais ces méthodes sont toujours implémentées sur la base d'un héritage unique.

8
nosid

Maintenant que les interfaces peuvent contenir du code exécutable, de nombreux cas d'utilisation pour les classes abstraites sont pris en charge par les interfaces. Mais les classes abstraites peuvent toujours avoir des variables membres, contrairement aux interfaces.

Le problème du diamant est évité en ne permettant tout simplement pas à une classe d'implémenter deux interfaces lorsque les deux interfaces fournissent une implémentation par défaut pour la même méthode avec la même signature.

5
Philipp

Java 8 introduit cependant des implémentations par défaut pour les interfaces, ce qui signifie que ce n'est plus la différence critique entre une interface et une classe abstraite.

Il y a encore quelques différences plus critiques. Reportez-vous à cet article:

Interface avec les méthodes par défaut vs classe abstraite dans Java 8

Comment les nouvelles interfaces Java 8 évitent le problème Diamond?

Cas 1: Vous implémentez deux interfaces, qui ont la même méthode default, vous devez résoudre le conflit dans votre implémentation clas s

interface interfaceA{
    default public void foo(){
        System.out.println("InterfaceA foo");
    }
}
interface interfaceB{
    default public void foo(){
        System.out.println("InterfaceB foo");
    }
}
public class DiamondExample implements interfaceA,interfaceB{
    public void foo(){
        interfaceA.super.foo();
    }
    public static void main(String args[]){
        new DiamondExample().foo();
    }
} 

L'exemple ci-dessus produit ci-dessous outout:

InterfaceA foo

Cas 2: Vous étendez une classe de base et implémentez une interface avec la méthode par défaut. Le compilateur résout le problème du diamant pour vous et vous n'avez pas à le résoudre comme dans le premier exemple.

interface interfaceA{
    default public void foo(){
        System.out.println("InterfaceA foo");
    }
}

class DiamondBase {
    public void foo(){
        System.out.println("Diamond base foo");
    }
}

public class DiamondExample extends DiamondBase implements interfaceA{

    public static void main(String args[]){
        new DiamondExample().foo();
    }
}

L'exemple ci-dessus produit ci-dessous la sortie:

Diamond base foo
4
Ravindra babu