Dans l'une de mes interviews, on m'a demandé d'expliquer la différence entre un Interface et un classe abstraite.
Voici ma réponse:
Les méthodes d'une interface Java sont implicitement abstraites et ne peuvent pas avoir d'implémentations. Une classe abstraite Java peut avoir des méthodes d'instance qui implémentent un comportement par défaut.
Les variables déclarées dans une interface Java sont finales par défaut. Une classe abstraite peut contenir des variables non finales.
Les membres d'une interface Java sont publics par défaut. Une classe abstraite Java peut avoir les saveurs habituelles des membres de la classe comme privée, protégée, etc.
Une interface Java doit être implémentée à l'aide du mot clé "implements"; Une classe abstraite Java doit être étendue à l'aide du mot-clé "extend".
Une interface ne peut étendre qu'une autre interface Java, une classe abstraite peut étendre une autre classe Java et implémenter plusieurs interfaces Java.
Une classe Java peut implémenter plusieurs interfaces, mais elle ne peut étendre qu'une classe abstraite.
Cependant, l'intervieweur n'était pas satisfait et m'a dit que cette description représentait " une connaissance livresque ".
Il m'a demandé une réponse plus pratique, expliquant quand je choisirais une classe abstraite sur une interface, en utilisant des exemples pratiques .
Où me suis-je trompé?
Je vais d'abord vous donner un exemple:
public interface LoginAuth{
public String encryptPassword(String pass);
public void checkDBforUser();
}
Supposons maintenant que vous ayez 3 bases de données dans votre application. Ensuite, chaque implémentation de cette base de données doit définir les 2 méthodes ci-dessus:
public class DBMySQL implements LoginAuth{
// Needs to implement both methods
}
public class DBOracle implements LoginAuth{
// Needs to implement both methods
}
public class DBAbc implements LoginAuth{
// Needs to implement both methods
}
Mais que se passe-t-il si encryptPassword () n'est pas dépendant de la base de données, et est-il le même pour chaque classe? Alors ce qui précède ne serait pas une bonne approche.
Au lieu de cela, considérez cette approche:
public abstract class LoginAuth{
public String encryptPassword(String pass){
// Implement the same default behavior here
// that is shared by all subclasses.
}
// Each subclass needs to provide their own implementation of this only:
public abstract void checkDBforUser();
}
Désormais, dans chaque classe enfant, il suffit d'implémenter une méthode - la méthode dépendante de la base de données.
J'ai fait de mon mieux et j'espère que cela effacera vos doutes.
Rien n'est parfait dans ce monde. Ils s'attendaient peut-être davantage à une approche pratique.
Mais après votre explication, vous pouvez ajouter ces lignes avec une approche légèrement différente.
Les interfaces sont des règles (règles parce que vous devez leur donner une implémentation que vous ne pouvez ni ignorer ni éviter, de sorte qu'elles s'imposent comme des règles) et qui fonctionnent comme un document de compréhension commun à diverses équipes de développement logiciel.
Les interfaces donnent une idée de ce qui doit être fait, mais pas comment. Donc, la mise en œuvre dépend complètement du développeur en respectant les règles données (moyen étant donné la signature des méthodes).
Les classes abstraites peuvent contenir des déclarations abstraites, des implémentations concrètes ou les deux.
Les déclarations abstraites sont comme des règles à suivre et les implémentations concrètes comme des instructions (vous pouvez les utiliser telles quelles ou les ignorer en les annulant et en leur donnant votre propre implémentation).
De plus, les méthodes avec la même signature qui peuvent changer le comportement dans différents contextes sont fournies sous forme de déclarations d'interface sous forme de règles à mettre en œuvre de manière correspondante dans différents contextes.
Edit: Java 8 facilite la définition de méthodes par défaut et statiques dans l'interface.
public interface SomeInterfaceOne {
void usualAbstractMethod(String inputString);
default void defaultMethod(String inputString){
System.out.println("Inside SomeInterfaceOne defaultMethod::"+inputString);
}
}
Désormais, lorsqu'une classe implémentera SomeInterface, il n'est pas obligatoire de fournir une implémentation pour les méthodes d'interface par défaut.
Si nous avons une autre interface avec les méthodes suivantes:
public interface SomeInterfaceTwo {
void usualAbstractMethod(String inputString);
default void defaultMethod(String inputString){
System.out.println("Inside SomeInterfaceTwo defaultMethod::"+inputString);
}
}
Java n'autorise pas l'extension de plusieurs classes car il en résulte "Problème Diamond" où le compilateur n'est pas en mesure de décider quelle méthode de superclasse utiliser. Avec les méthodes par défaut, le problème de diamant se posera aussi pour les interfaces. Parce que si une classe implémente à la fois
SomeInterfaceOne and SomeInterfaceTwo
et n’implémente pas la méthode commune par défaut, le compilateur ne peut pas choisir laquelle choisir. Pour éviter ce problème, dans Java 8, il est obligatoire d'implémenter des méthodes par défaut communes pour différentes interfaces. Si une classe implémente les deux interfaces ci-dessus, elle doit fournir l'implémentation de la méthode defaultMethod (), sinon le compilateur générera une erreur de temps de compilation.
Vous avez bien résumé les différences pratiques d'utilisation et de mise en œuvre mais vous n'avez rien dit sur la différence de sens.
Une interface est une description du comportement d’une classe d’implémentation. La classe d'implémentation garantit qu'elle disposera de ces méthodes pouvant être utilisées. C'est fondamentalement un contrat ou une promesse que la classe doit faire.
Une classe abstraite sert de base à différentes sous-classes partageant un comportement qui n'a pas besoin d'être créé de manière répétée. Les sous-classes doivent terminer le comportement et avoir la possibilité de remplacer le comportement prédéfini (tant qu'il n'est pas défini comme final
ou private
).
Vous trouverez de bons exemples dans le package Java.util
, qui inclut des interfaces telles que List
et des classes abstraites, telles que AbstractList
, qui implémente déjà l'interface. Le documentation officielle décrit la AbstractList
comme suit:
Cette classe fournit une implémentation squelettique de l’interface List afin de minimiser l’effort requis pour implémenter cette interface reposant sur un magasin de données à "accès aléatoire" (tel qu’un tableau).
Une interface est composée de variables singleton (public static final) et de méthodes abstraites publiques. Nous préférons normalement utiliser une interface en temps réel quand nous savons ce qu'il faut faire mais ne sais pas comment le faire .
Ce concept peut être mieux compris par exemple:
Considérons une classe de paiement. Le paiement peut être effectué de différentes manières, telles que Paypal, carte de crédit, etc. Nous prenons donc normalement Payment comme interface qui contient une méthode makePayment()
, ainsi que CreditCard et Paypal sont les deux classes d'implémentation.
public interface Payment
{
void makePayment();//by default it is a abstract method
}
public class Paypal implements Payment
{
public void makePayment()
{
//some logic for Paypal payment
//e.g. Paypal uses username and password for payment
}
}
public class CreditCard implements Payment
{
public void makePayment()
{
//some logic for CreditCard payment
//e.g. CreditCard uses card number, date of expiry etc...
}
}
Dans l'exemple ci-dessus, CreditCard et Paypal sont deux stratégies/classes de mise en œuvre. Une interface nous permet également le concept d'héritage multiple dans Java qui ne peut pas être accompli par une classe abstraite.
Nous choisissons une classe abstraite quand il y a des fonctionnalités pour lesquelles nous savons quoi faire, et d'autres que nous savons comment exécuter .
Prenons l'exemple suivant:
public abstract class Burger
{
public void packing()
{
//some logic for packing a burger
}
public abstract void price(); //price is different for different categories of burgers
}
public class VegBerger extends Burger
{
public void price()
{
//set price for a veg burger.
}
}
public class NonVegBerger extends Burger
{
public void price()
{
//set price for a non-veg burger.
}
}
Si nous ajoutons à l'avenir des méthodes (concrètes/abstraites) à une classe abstraite donnée, la classe d'implémentation n'aura pas besoin de changer son code. Cependant, si nous ajoutons des méthodes dans une interface à l'avenir, nous devons ajouter des implémentations à toutes les classes ayant implémenté cette interface, sinon des erreurs de compilation surviennent.
Il existe d’autres différences, mais il s’agit de différences majeures que votre intervieweur aurait peut-être attendues. J'espère que cela a été utile.
Les modifications d'interface Java 8 incluent des méthodes statiques et des méthodes par défaut dans les interfaces. Avant Java 8, nous ne pouvions avoir que des déclarations de méthodes dans les interfaces. Mais à partir de Java 8, nous pouvons avoir des méthodes par défaut et des méthodes statiques dans les interfaces.
Après avoir introduit la méthode par défaut, il semble que les interfaces et les classes abstraites soient identiques. Cependant, ils sont toujours différents dans Java 8.
La classe abstraite peut définir un constructeur. Ils sont plus structurés et peuvent être associés à un état. En revanche, la méthode par défaut ne peut être implémentée qu'en invoquant d'autres méthodes d'interface, sans référence à l'état d'une implémentation particulière. Par conséquent, les deux utilisations à des fins différentes et le choix entre deux dépend du contexte du scénario.
Les classes abstraites sont valides pour les implémentations d'interfaces squelettiques (c'est-à-dire partielles) mais ne devraient pas exister sans interface correspondante.
Ainsi, lorsque les classes abstraites sont effectivement réduites à une faible visibilité et à des implémentations squelettiques d’interfaces, les méthodes par défaut peuvent-elles également éliminer cela? Décidément: non! L'implémentation d'interfaces nécessite presque toujours tout ou partie de ces outils de construction de classes qui manquent aux méthodes par défaut. Et si une interface ne le fait pas, il s’agit clairement d’un cas particulier qui ne doit pas vous égarer.
Java 8 introduit la nouvelle fonctionnalité “ Méthode par défaut ” ou (méthodes Defender), qui permet au développeur d’ajouter de nouvelles méthodes aux interfaces sans rompre l’implémentation existante de ces interfaces. Il offre la possibilité d'autoriser l'implémentation Interface define qui sera utilisée par défaut dans le cas où une classe concrète ne parviendrait pas à fournir une implémentation pour cette méthode.
Voyons un petit exemple pour comprendre comment cela fonctionne:
public interface OldInterface {
public void existingMethod();
default public void newDefaultMethod() {
System.out.println("New default method"
+ " is added in interface");
}
}
La classe suivante sera compilée avec succès dans Java JDK 8,
public class OldInterfaceImpl implements OldInterface {
public void existingMethod() {
// existing implementation is here…
}
}
Si vous créez une instance de OldInterfaceImpl:
OldInterfaceImpl obj = new OldInterfaceImpl ();
// print “New default method add in interface”
obj.newDefaultMethod();
Les méthodes par défaut ne sont jamais finales, ne peuvent pas être synchronisées et ne peuvent pas écraser les méthodes d’Object. Ils sont toujours publics, ce qui limite considérablement la possibilité d'écrire des méthodes courtes et réutilisables.
Les méthodes par défaut peuvent être fournies à une interface sans affecter l'implémentation des classes car elle inclut une implémentation. Si chaque méthode ajoutée dans une interface définie avec une implémentation, aucune implémentation de classe n'est affectée. Une classe d'implémentation peut remplacer l'implémentation par défaut fournie par l'interface.
Les méthodes par défaut permettent d'ajouter de nouvelles fonctionnalités aux interfaces existantes sans rompre la mise en œuvre plus ancienne de ces interfaces.
Lorsque nous étendons une interface contenant une méthode par défaut, nous pouvons effectuer les opérations suivantes:
Pour Java 8, les collections JDK ont été étendues et la méthode forEach est ajoutée à l'ensemble de la collection (qui fonctionne conjointement avec lambdas). Avec la méthode conventionnelle, le code ressemble à celui ci-dessous,
public interface Iterable<T> {
public void forEach(Consumer<? super T> consumer);
}
Étant donné que ce résultat implémente donc chaque classe avec des erreurs de compilation, une méthode par défaut est ajoutée avec une implémentation requise afin que l'implémentation existante ne soit pas modifiée.
L’interface Iterable avec la méthode Default est ci-dessous,
public interface Iterable<T> {
public default void forEach(Consumer
<? super T> consumer) {
for (T t : this) {
consumer.accept(t);
}
}
}
Le même mécanisme a été utilisé pour ajouter Stream dans l'interface JDK sans interrompre les classes implémentées.
Étant donné que Java Class peut implémenter plusieurs interfaces et que chaque interface peut définir une méthode par défaut avec la même signature de méthode, les méthodes héritées peuvent donc être en conflit.
Considérons ci-dessous l'exemple,
public interface InterfaceA {
default void defaultMethod(){
System.out.println("Interface A default method");
}
}
public interface InterfaceB {
default void defaultMethod(){
System.out.println("Interface B default method");
}
}
public class Impl implements InterfaceA, InterfaceB {
}
Le code ci-dessus ne pourra pas être compilé avec l'erreur suivante,
Java: la classe Impl hérite des valeurs par défaut non liées pour defaultMethod () des types InterfaceA et InterfaceB
Afin de corriger cette classe, nous devons fournir une implémentation de méthode par défaut:
public class Impl implements InterfaceA, InterfaceB {
public void defaultMethod(){
}
}
De plus, si nous voulons invoquer une implémentation par défaut fournie par l’une des super interfaces plutôt que par notre propre implémentation, nous pouvons le faire comme suit:
public class Impl implements InterfaceA, InterfaceB {
public void defaultMethod(){
// existing code here..
InterfaceA.super.defaultMethod();
}
}
Nous pouvons choisir n'importe quelle implémentation par défaut ou les deux dans le cadre de notre nouvelle méthode.
Méthode statique d'interface Java, exemple de code, méthode statique vs méthode par défaut
La méthode statique d'interface Java est similaire à la méthode par défaut, à la différence que nous ne pouvons pas les remplacer dans les classes d'implémentation. Cette fonctionnalité nous aide à éviter des résultats indésirables en cas d'implémentation médiocre dans les classes d'implémentation. Voyons cela avec un exemple simple.
public interface MyData {
default void print(String str) {
if (!isNull(str))
System.out.println("MyData Print::" + str);
}
static boolean isNull(String str) {
System.out.println("Interface Null Check");
return str == null ? true : "".equals(str) ? true : false;
}
}
Voyons maintenant une classe d'implémentation dont la méthode isNull () est mal implémentée.
public class MyDataImpl implements MyData {
public boolean isNull(String str) {
System.out.println("Impl Null Check");
return str == null ? true : false;
}
public static void main(String args[]){
MyDataImpl obj = new MyDataImpl();
obj.print("");
obj.isNull("abc");
}
}
Notez que isNull (String str) est une méthode de classe simple. Elle ne remplace pas la méthode d’interface. Par exemple, si nous ajoutons une annotation @Override à la méthode isNull (), cela entraînera une erreur du compilateur.
Maintenant, lorsque nous allons exécuter l'application, nous obtenons la sortie suivante.
Vérification de l'interface
Impl Null Check
Si nous faisons passer la méthode d'interface de static à default, nous obtiendrons le résultat suivant.
Impl Null Check
MyData Print ::
Impl Null Check
La méthode statique d'interface Java n'est visible que pour les méthodes d'interface. Si nous supprimons la méthode isNull () de la classe MyDataImpl, nous ne pourrons pas l'utiliser pour l'objet MyDataImpl. Cependant, comme d'autres méthodes statiques, nous pouvons utiliser des méthodes statiques d'interface en utilisant le nom de la classe. Par exemple, une déclaration valide sera:
boolean result = MyData.isNull("abc");
Avant de terminer mon article, j'aimerais vous présenter une brève introduction aux interfaces fonctionnelles. Une interface avec exactement une méthode abstraite est connue sous le nom d’interface fonctionnelle.
Une nouvelle annotation @FunctionalInterface
a été introduite pour marquer une interface en tant qu'interface fonctionnelle. L'annotation @FunctionalInterface
permet d'éviter l'ajout accidentel de méthodes abstraites dans les interfaces fonctionnelles. C’est facultatif mais c’est une bonne pratique de l’utiliser.
Les interfaces fonctionnelles sont attendues depuis longtemps et sont une fonctionnalité très recherchée de Java 8 car elles nous permettent d’utiliser des expressions lambda pour les instancier. Un nouveau package Java.util.function avec un ensemble d'interfaces fonctionnelles est ajouté pour fournir les types de cibles pour les expressions lambda et les références de méthodes. Nous examinerons les interfaces fonctionnelles et les expressions lambda dans les prochains articles.
Toutes vos instructions sont valides à l'exception de votre première instruction (après la version Java 8):
Les méthodes d'une interface Java sont implicitement abstraites et ne peuvent pas avoir d'implémentations
De la documentation page :
Une interface est un type de référence, similaire à une classe, qui ne peut contenir que des constantes , des signatures de méthodes, des méthodes par défaut, des méthodes statiques et des types imbriqués .
Les corps de méthodes n'existent que pour les méthodes par défaut et les méthodes statiques.
Méthodes par défaut:
Une interface peut avoir méthodes par défaut , mais est différente des méthodes abstraites dans les classes abstraites.
Les méthodes par défaut vous permettent d'ajouter de nouvelles fonctionnalités aux interfaces de vos bibliothèques et d'assurer la compatibilité binaire avec le code écrit pour les versions antérieures de ces interfaces.
Lorsque vous étendez une interface contenant une méthode par défaut, vous pouvez effectuer les opérations suivantes:
abstract
.Méthodes statiques:
En plus des méthodes par défaut, vous pouvez définir des méthodes statiques dans les interfaces. (Une méthode statique est une méthode associée à la classe dans laquelle elle est définie plutôt qu'à un objet. Chaque instance de la classe partage ses méthodes statiques.)
Cela facilite l'organisation des méthodes d'assistance dans vos bibliothèques.
Exemple de code tiré de la page de documentation relative à interface
ayant les méthodes static
et default
.
import Java.time.*;
public interface TimeClient {
void setTime(int hour, int minute, int second);
void setDate(int day, int month, int year);
void setDateAndTime(int day, int month, int year,
int hour, int minute, int second);
LocalDateTime getLocalDateTime();
static ZoneId getZoneId (String zoneString) {
try {
return ZoneId.of(zoneString);
} catch (DateTimeException e) {
System.err.println("Invalid time zone: " + zoneString +
"; using default time zone instead.");
return ZoneId.systemDefault();
}
}
default ZonedDateTime getZonedDateTime(String zoneString) {
return ZonedDateTime.of(getLocalDateTime(), getZoneId(zoneString));
}
}
Utilisez les instructions ci-dessous pour choisir d'utiliser une interface ou une classe abstraite.
Interface:
Classe abstraite:
Partager le code entre plusieurs classes étroitement liées. Il établit est une relation .
Partager un état commun entre des classes associées (cet état peut être modifié dans des classes concrètes)
Articles Similaires:
Interface vs classe abstraite (OO général)
Implements vs extend: Quand utiliser? Quelle est la différence?
En parcourant ces exemples, vous pouvez comprendre que
Les classes non apparentées peuvent avoir des fonctionnalités via l'interface, mais les classes associées modifient le comportement via l'extension des classes de base.
Votre explication semble correcte, mais il se peut que vous ayez tout lu dans un manuel. : - /
Ce qui me préoccupe le plus, c'est de savoir à quel point votre exemple était solide. Avez-vous pris la peine d'inclure presque toutes les différences entre abstrait et interfaces?
Personnellement, je suggérerais ce lien: http://mindprod.com/jgloss/interfacevsabstract.html#TABLE
pour une liste exhaustive des différences ..
J'espère que cela vous aidera, ainsi que tous les autres lecteurs, dans leurs futures interviews
De nombreux développeurs débutants commettent l'erreur de penser que les interfaces, les classes abstraites et les classes concrètes sont de légères variations de la même chose, et choisissent l'une d'entre elles uniquement pour des raisons techniques: Ai-je besoin d'un héritage multiple? Ai-je besoin d'un endroit pour mettre des méthodes communes? Dois-je m'embêter avec autre chose qu'un cours concret? C'est faux et le problème principal est caché dans ces questions: "I". Lorsque vous écrivez du code pour vous-même, vous pensez rarement à d'autres développeurs, actuels ou futurs, qui travaillent sur ou avec votre code.
Les interfaces et les classes abstraites, bien qu'apparemment similaires d'un point de vue technique, ont des significations et des finalités complètement différentes.
Une interface définit un contrat qu'une implémentation remplira pour vous .
Une classe abstraite fournit un comportement par défaut que votre implémentation peut réutiliser.
Ce que je recherche lors de l’interview, c’est ces deux points ci-dessus. C’est un résumé assez compact. Lire la suite pour plus de détails.
En d'autres termes: une classe concrète effectue le travail réel, de manière très spécifique. Par exemple, un ArrayList
utilise une zone de mémoire contiguë pour stocker une liste d'objets de manière compacte, offrant un accès aléatoire rapide, des itérations et des modifications sur place, mais effroyable lors de l'insertion, de la suppression et parfois même de la suppression. des ajouts; Pendant ce temps, un LinkedList
utilise des nœuds à double liaison pour stocker une liste d'objets, ce qui offre une itération rapide, des modifications sur place et une insertion/suppression/addition, mais est terrible en accès aléatoire. Ces deux types de listes sont optimisés pour différents cas d'utilisation, et la manière dont vous les utiliserez importera beaucoup. Lorsque vous essayez d'extraire les performances d'une liste avec laquelle vous êtes en forte interaction et lorsque vous choisissez le type de liste, vous devez choisir avec soin celle que vous instanciez.
D'un autre côté, les utilisateurs de haut niveau d'une liste ne se soucient pas vraiment de la manière dont elle est réellement mise en œuvre, et ils devraient être isolés de ces détails. Imaginons que Java n'ait pas exposé l'interface List
, mais seulement une classe concrète List
qui est en fait ce que LinkedList
est actuellement. Tous les développeurs Java auraient personnalisé leur code pour l'adapter aux détails de la mise en œuvre: évitez les accès aléatoires, ajoutez un cache pour accélérer les accès ou réimplémentez simplement ArrayList
, bien que cela soit incompatible avec tous les autres codes qui fonctionnent réellement avec List
uniquement. Ce serait terrible ... Mais maintenant, imaginez que les maîtres Java réalisent bien qu'une liste chaînée est terrible pour la plupart des cas d'utilisation réels et ont décidé de passer à une liste de tableaux pour leur unique List
classe disponible. Cela affecterait les performances de chaque programme Java dans le monde, et les gens n'en seraient pas contents. Et le principal responsable est que les détails de la mise en œuvre étaient disponibles, et les développeurs ont supposé que ces détails constituaient un contrat permanent sur lequel ils pouvaient compter. C'est pourquoi il est important de masquer les détails de l'implémentation et de définir uniquement un contrat abstrait. C’est le but d’une interface: définir le type d’entrée accepté par une méthode et le type de sortie attendu, sans exposer tout le courage qui inciterait les programmeurs à ajuster leur code pour qu’il corresponde aux détails internes susceptibles de changer lors d’une mise à jour ultérieure. .
Une classe abstraite est au milieu entre les interfaces et les classes concrètes. Il est supposé aider les implémentations à partager du code commun ou ennuyeux. Par exemple, AbstractCollection
fournit des implémentations de base pour isEmpty
en fonction de la taille est 0, contains
en itérant et comparant, addAll
en tant que répétée add
, etc. Cela permet aux implémentations de se concentrer sur les éléments cruciaux qui les différencient: comment stocker et récupérer des données.
Les interfaces ont une faible cohésion passerelles entre différentes parties du code. Ils permettent aux bibliothèques d'exister et d'évoluer sans détruire chaque utilisateur de bibliothèque lorsque quelque chose change en interne. Cela s'appelle Programmation d'applications Interface, pas de classes de programmation d'applications. Sur une plus petite échelle, ils permettent également à plusieurs développeurs de collaborer avec succès sur des projets de grande envergure, en séparant différents modules via des interfaces bien documentées.
Les classes abstraites sont à forte cohésion aides à utiliser lors de la mise en oeuvre d'une interface, en supposant un certain niveau de détails de mise en œuvre. Alternativement, les classes abstraites sont utilisées pour définir les SPI, interfaces de fournisseur de service.
La différence entre une API et un SPI est subtile, mais importante: pour une API, l'accent est mis sur qui tilise, et pour un SPI l'accent est mis sur qui implémente le.
L'ajout de méthodes à une API est facile, tous les utilisateurs existants de cette API seront toujours compilés. Ajouter des méthodes à un SPI est difficile, car chaque fournisseur de services (implémentation concrète) devra implémenter les nouvelles méthodes. Si des interfaces sont utilisées pour définir un SPI, un fournisseur devra publier une nouvelle version à chaque modification du contrat SPI. Si des classes abstraites sont utilisées à la place, les nouvelles méthodes peuvent soit être définies en termes de méthodes abstraites existantes, soit sous forme de stubs vides throw not implemented exception
, ce qui permettra au moins à une version antérieure d'une implémentation de service de toujours être compilée et exécutée.
Bien que Java 8 ait introduit les méthodes par défaut pour les interfaces, ce qui rend la ligne entre les interfaces et les classes abstraites encore plus floue, ce qui ne permettait pas aux implémentations de réutiliser du code, mais facilitait le changement d'interfaces une API et en tant que SPI (ou sont utilisés à tort pour définir des SPI au lieu de classes abstraites).
Les détails techniques fournis dans la réponse du PO sont considérés comme une "connaissance du livre" car il s'agit généralement de l'approche utilisée à l'école et dans la plupart des ouvrages sur la technologie concernant une langue: quoi une chose est, pas comment à utiliser dans la pratique, en particulier dans les applications à grande échelle.
Voici une analogie: supposons que la question était:
Quoi de mieux de louer pour la nuit de bal d'étudiants, une voiture ou une chambre d'hôtel?
La réponse technique ressemble à ceci:
Eh bien, dans une voiture, vous pouvez le faire plus tôt, mais dans une chambre d'hôtel, vous pouvez le faire plus confortablement. D'autre part, la chambre d'hôtel n'est située qu'à un seul endroit, tandis que dans la voiture, vous pouvez le faire à plusieurs endroits, comme, disons, que vous pouvez aller au point Vista pour une belle vue ou dans un cinéma en voiture. ou beaucoup d'autres endroits, ou même dans plus d'un endroit. En outre, la chambre d'hôtel a une douche.
Tout cela est vrai, mais il manque complètement le point qu’il s’agit de deux choses complètement différentes et que les deux peuvent être utilisées en même temps à des fins différentes, et le fait de le faire n’est pas le facteur le plus important dans l’une ou l’autre des deux options. . La réponse manque de perspective, elle montre une façon de penser immature, tout en présentant correctement les "faits" réels.
Une interface est un "contrat" dans lequel la classe qui implémente le contrat s'engage à implémenter les méthodes. Un exemple où j'ai dû écrire une interface au lieu d'une classe était lorsque je mettais à niveau un jeu de la 2D à la 3D. Je devais créer une interface pour partager des classes entre les versions 2D et 3D du jeu.
package adventure;
import Java.awt.*;
public interface Playable {
public void playSound(String s);
public Image loadPicture(String s);
}
Ensuite, je peux implémenter les méthodes basées sur l'environnement, tout en pouvant appeler ces méthodes depuis un objet qui ne sait pas quelle version du jeu est en cours de chargement.
public class Adventure extends JFrame implements Playable
public class Dungeon3D extends SimpleApplication implements Playable
public class Main extends SimpleApplication implements AnimEventListener, ActionListener, Playable
Typiquement, dans le monde du jeu, le monde peut être une classe abstraite qui exécute des méthodes sur le jeu:
public abstract class World...
public Playable owner;
public Playable getOwner() {
return owner;
}
public void setOwner(Playable owner) {
this.owner = owner;
}
Qu'en est-il de penser de la manière suivante:
Ainsi, lorsque vous avez une classe abstraite Mammals, une sous-classe Human et une interface Driving, vous pouvez dire
Ma suggestion est que la phrase de connaissance du livre indique qu'il voulait entendre la différence sémantique entre les deux (comme d'autres déjà suggérées).
Les classes abstraites ne sont pas de l'abstraction pure, car sa collection de bétons (méthodes implémentées) ainsi que de méthodes non implémentées. Mais les interfaces sont de l'abstraction pure, car il n'y a que des méthodes non mises en œuvre, pas des méthodes concrètes.
Pourquoi les classes abstraites?
Pourquoi les interfaces?
Une interface est comme un ensemble de gènes documentés publiquement comme ayant un effet quelconque: un test ADN me dira si je les ai - et si je le fais, je peux publiquement faire savoir que je suis un "transporteur" et qu'une partie de mon comportement ou de mon état s'y conformera. (Mais bien sûr, il se peut que de nombreux autres gènes fournissent des traits en dehors de cette portée.)
Une classe abstraite est comme l’ancêtre décédé d’une espèce homosexuelle (*): elle ne peut être amenée à la vie mais un descendant vivant (c'est-à-dire non abstrait) hérite de tous ses gènes.
(*) Pour étirer cette métaphore, supposons que tous les membres de l'espèce vivent au même âge. Cela signifie que tous les ancêtres d'un ancêtre décédé doivent également être morts - de même que tous les descendants d'un ancêtre vivant doivent être en vie.
Je fais des entretiens pour le travail et j’aimerais aussi avoir une réponse défavorable à votre réponse (désolé mais très honnête). On dirait que vous avez lu la différence et révisé une réponse, mais vous ne l’avez peut-être jamais utilisée dans la pratique.
Une bonne explication de la raison pour laquelle vous utiliseriez chacun peut être bien meilleure que d'avoir une explication précise de la différence. Les employeurs veulent finalement que les programmeurs fassent des choses qu'ils ne connaissent pas, ce qui peut être difficile à démontrer en entrevue. La réponse que vous avez donnée serait utile si vous postulez pour un travail technique ou basé sur la documentation, mais pas pour un rôle de développeur.
Bonne chance avec des interviews dans le futur.
De plus, ma réponse à cette question concerne davantage la technique d’entretien que le matériel technique que vous avez fourni. Peut-être envisager de lire à ce sujet. https://workplace.stackexchange.com/ peut être un excellent endroit pour ce genre de chose.
Une interface est purement abstraite. nous n'avons pas de code d'implémentation dans l'interface.
La classe abstraite contient les méthodes et son implémentation.
cliquez ici pour regarder le tutoriel sur les interfaces et les classes abstraites
La différence principale que j’ai observée est que la classe abstraite nous fournit un comportement commun déjà implémenté et que les sous-classes n’ont besoin que d’implémenter des fonctionnalités spécifiques qui leur correspondent. où comme pour une interface ne spécifiera que les tâches à effectuer et aucune implémentation ne sera donnée par interface. Je peux dire qu'il spécifie le contrat entre lui-même et les classes implémentées.
Même si j'ai rencontré la même question dans plusieurs interviews et croyez-moi, votre temps est misérable pour convaincre l'intervieweur. Si toutes les réponses ci-dessus sont inhérentes, il me faut ajouter un point clé pour le rendre plus convaincant et utiliser au mieux OO
Au cas où vous n'êtes pas planifier toute modification dans les règles , pour la sous-classe à suivre, pour un long avenir, choisissez l'interface, car vous ne pourrez pas la modifier et si vous le faites, vous devez rechercher les modifications dans toutes les autres sous-classes, alors que, si tu penses, vous souhaitez réutiliser les fonctionnalités, définir des règles et les rendre ouvertes pour modification, optez pour la classe abstraite.
Pensez ainsi, vous avez utilisé un service consommable ou vous avez fourni du code à world et vous avez une chance de modifier quelque chose, de supposer un contrôle de sécurité et si je suis un utilisateur du code et un matin après une mise à jour, trouver toutes les marques lues dans mon Eclipse, toute l'application est en panne. Donc, pour éviter de tels cauchemars, utilisez Abstract sur les interfaces
Je pense que cela pourrait convaincre l'intervieweur dans une certaine mesure ... Bonne entrevues à venir.
Vous choisissez Interface dans Java pour éviter le problème de diamant dans l'héritage multiple .
Si vous souhaitez que toutes vos méthodes soient implémentées par votre client, vous optez pour l'interface. Cela signifie que vous concevez l'intégralité de l'application en résumé.
Vous choisissez la classe abstraite si vous savez déjà ce qui est en commun. Par exemple, prenez une classe abstraite Car
. Au niveau supérieur, vous implémentez les méthodes de voiture courantes telles que calculateRPM()
. C’est une méthode courante et vous laissez le client implémenter son propre comportement commecalculateMaxSpeed()
etc. Vous auriez probablement expliqué en donnant quelques exemples en temps réel que vous avez rencontrés dans votre travail quotidien.
Lorsque j'essaie de partager un comportement entre deux classes étroitement liées, je crée une classe abstraite contenant le comportement commun et servant de parent aux deux classes.
Lorsque je tente de définir un type, une liste de méthodes qu'un utilisateur de mon objet peut utiliser de manière fiable, je crée alors une interface.
Par exemple, je ne créerais jamais une classe abstraite avec 1 sous-classe concrète car les classes abstraites concernent le comportement de partage. Mais je pourrais très bien créer une interface avec une seule implémentation. L'utilisateur de mon code ne saura pas qu'il existe une seule implémentation. En effet, dans une future version, il pourrait y avoir plusieurs implémentations, qui seraient toutes des sous-classes d'une nouvelle classe abstraite qui n'existait même pas lorsque j'ai créé l'interface.
Cela aurait peut-être semblé un peu trop livresque (même si je ne l’ai jamais vu ainsi). Si l'enquêteur (ou le PO) voulait vraiment davantage de mon expérience personnelle à ce sujet, j'aurais été prêt avec des anecdotes d'une interface qui a évolué par nécessité et vice versa.
Encore une chose. Java 8 vous permet maintenant de mettre du code par défaut dans une interface, rendant plus floue la ligne entre les interfaces et les classes abstraites. Mais d'après ce que j'ai vu, cette fonctionnalité est surexploitée même par les créateurs des bibliothèques de base Java. Cette fonctionnalité a été ajoutée, à juste titre, pour permettre l’extension d’une interface sans créer d’incompatibilité binaire. Mais si vous créez un nouveau type en définissant une interface, l'interface doit alors être JUST une interface. Si vous souhaitez également fournir un code commun, créez une classe d'assistance (abstraite ou concrète). N'encombrez pas votre interface dès le départ avec des fonctionnalités que vous voudrez peut-être modifier.
La différence fondamentale entre l'interface et la classe abstraite est que l'interface prend en charge l'héritage multiple, mais pas la classe abstraite.
Dans la classe abstraite, vous pouvez également fournir toutes les méthodes abstraites telles que l'interface.
pourquoi la classe abstraite est nécessaire?
Dans certains scénarios, lors du traitement de la demande de l'utilisateur, la classe abstraite ne sait pas quelle est l'intention de l'utilisateur. Dans ce scénario, nous définirons une méthode abstraite dans la classe et demanderons à l'utilisateur qui étend cette classe d'indiquer votre intention dans la méthode abstraite. Dans ce cas, les classes abstraites sont très utiles
Pourquoi l'interface est nécessaire?
Disons que j'ai un travail que je n'ai pas d'expérience dans ce domaine. Exemple, si vous voulez construire un bâtiment ou un barrage, que ferez-vous dans ce scénario?
Ici, je ne me soucie pas de la logique de leur construction. L'objet final a satisfait mes exigences ou pas, c'est seulement mon point clé.
Ici, vos exigences appelées interface et constructeurs sont appelées implémenteur.
Je vais essayer de répondre en utilisant un scénario pratique pour montrer la distinction entre les deux.
Les interfaces ne génèrent aucune charge utile, c’est-à-dire qu’aucun état n’a besoin d’être maintenu; il est donc préférable de n’associer qu’un contrat (capacité) à une classe.
Par exemple, disons que j'ai une classe Task qui exécute une action. Maintenant, pour exécuter une tâche dans un thread séparé, je n'ai pas vraiment besoin d'étendre la classe Thread. Le meilleur choix est de faire en sorte que Task implémente l'interface Runnable (c'est-à-dire implémente sa méthode run () ) puis passez l'objet de cette classe Task à une instance de Thread et appelez sa méthode start ().
Maintenant, vous pouvez demander si Runnable était une classe abstraite?
Techniquement, c’était possible, mais en ce qui concerne la conception, c’était une mauvaise raison:
En d'autres termes, la classe de tâches avait besoin d'une capacité à exécuter dans un thread, ce qu'elle a réalisé en implémentant des versets d'interface Runnable étendant la classe Thread, ce qui en ferait un thread.
Il suffit de nous mettre interface pour définir une capacité (contrat), tout en utilisant une classe abstraite pour définir sa mise en oeuvre squelette (commune/partielle).
Disclaimer: exemple stupide suit, essayez de ne pas juger :-P
interface Forgiver {
void forgive();
}
abstract class GodLike implements Forgiver {
abstract void forget();
final void forgive() {
forget();
}
}
Maintenant, on vous a donné le choix d'être à la manière de Dieu, mais vous pouvez choisir de ne pardonner que (c'est-à-dire pas de Dieu) et faire:
class HumanLike implements Forgiver {
void forgive() {
// forgive but remember
}
}
Ou vous pouvez choisir d'être à la manière de Dieu et faire:
class AngelLike extends GodLike {
void forget() {
// forget to forgive
}
}
P.S. avec Java 8 interface peut également avoir des méthodes statiques ainsi que des méthodes par défaut (implémentation pouvant être remplacée), de sorte que la différence entre l'interface b/w et la classe abstraite est encore plus réduite.
Oui, vos réponses étaient techniquement correctes, mais vous vous êtes trompé en ne leur montrant pas que vous comprenez les avantages et les inconvénients de choisir l’un sur l’autre. En outre, ils étaient probablement inquiets/paniqués à propos de la compatibilité de leur base de code avec les mises à niveau futures. Ce type de réponse peut avoir aidé (en plus de ce que vous avez dit):
"Le choix d'une classe abstraite sur une classe d'interface dépend de ce que nous projetons pour l'avenir du code.
Les classes abstraites permettent une meilleure compatibilité en aval, car vous pouvez continuer à ajouter un comportement à une classe abstraite à l'avenir sans altérer votre code existant -> cela n'est pas possible avec une classe d'interface.
D'autre part, les classes d'interface sont plus flexibles que les classes abstraites. C'est parce qu'ils peuvent implémenter plusieurs interfaces. Le problème est que Java ne possède pas plusieurs héritages. L'utilisation de classes abstraites ne vous permettra donc pas d'utiliser une autre structure hiérarchique de classes ...
Donc, en fin de compte, une bonne règle générale est la suivante: préférez utiliser les classes d'interface lorsqu'il n'y a pas d'implémentations existantes/par défaut dans votre base de code. Et utilisez des classes abstraites pour préserver la compatibilité si vous savez que vous mettrez à jour votre classe ultérieurement. "
Bonne chance pour votre prochaine interview!
hmm maintenant les gens ont faim approche pratique, vous avez tout à fait raison mais la plupart des interviewers ont l’air conforme à leurs exigences actuelles et veulent une approche pratique.
après avoir terminé votre réponse, vous devriez sauter sur l'exemple:
Résumé:
par exemple, nous avons une fonction de salaire qui a des paramètres communs à tous les employés. alors nous pouvons avoir une classe abstraite appelée CTC avec un corps de méthode partiellement défini et elle sera étendue par tous les types d’employés et sera redéfinie selon leurs capacités supplémentaires. Pour une fonctionnalité commune.
public abstract class CTC {
public int salary(int hra, int da, int extra)
{
int total;
total = hra+da+extra;
//incentive for specific performing employee
//total = hra+da+extra+incentive;
return total;
}
}
class Manger extends CTC
{
}
class CEO extends CTC
{
}
class Developer extends CTC
{
}
Interface
interface dans Java permet d’avoir une fonctionnalité interfcae sans l’étendre et vous devez être clair avec l’implémentation de la signature de la fonctionnalité que vous souhaitez introduire dans votre application. cela vous obligera à avoir une définition. Pour des fonctionnalités différentes.
public interface EmployeType {
public String typeOfEmployee();
}
class ContarctOne implements EmployeType
{
@Override
public String typeOfEmployee() {
return "contract";
}
}
class PermanentOne implements EmployeType
{
@Override
public String typeOfEmployee() {
return "permanent";
}
}
vous pouvez aussi avoir une telle activité forcée avec une classe abstraite en définissant une méthode abstraite, maintenant une classe qui étend la classe abstraite remin abstraite jusqu'à ce qu'elle écrase cette fonction abstraite.
Presque tout semble déjà être couvert ici déjà. Ajouter un dernier point sur la mise en œuvre pratique de la classe abstract
:
Le mot clé
abstract
est également utilisé pour empêcher l'instanciation d'une classe. Si vous avez une classe concrète que vous ne voulez pas instancier, indiquezabstract
.
D'après ce que je comprends et comment j'aborde,
L'interface est comme une spécification/un contrat, toute classe qui implémente une classe d'interface doit implémenter toutes les méthodes définies dans la classe abstraite (à l'exception des méthodes par défaut (introduites dans Java 8))
Alors que je définis un résumé de classe lorsque je connais l’implémentation requise pour certaines méthodes de la classe et certaines méthodes, je ne sais toujours pas ce que sera l’implémentation (nous pourrions connaître la signature de fonction mais pas l’implémentation). Je le fais de sorte que plus tard dans la partie du développement, quand je sais comment ces méthodes doivent être implémentées, je peux simplement étendre cette classe abstraite et implémenter ces méthodes.
Remarque: Vous ne pouvez pas avoir le corps de la fonction dans les méthodes d'interface à moins que la méthode soit statique ou par défaut.
En classe abstraite, vous pouvez écrire l'implémentation par défaut des méthodes! Mais dans l'interface, vous ne pouvez pas. En principe, dans interface, il existe des méthodes virtuelles pures qui doivent être implémentées par la classe qui implémente l'interface.
D'après ce que j'ai compris, une interface, qui comprend des variables finales et des méthodes sans implémentation, est implémentée par une classe pour obtenir un groupe de méthodes ou des méthodes liées les unes aux autres. D'autre part, une classe abstraite, qui peut contenir des variables non finales et des méthodes avec implémentations, est généralement utilisée comme guide ou superclasse dont toutes les classes associées ou similaires héritent. En d'autres termes, une classe abstraite contient toutes les méthodes/variables partagées par toutes ses sous-classes.
Je pense que l'intervieweur essayait de comprendre la différence entre l'interface et la mise en œuvre.
L’interface - pas une interface Java, mais une "interface" plus généralement - avec un module de code est, en gros, le contrat conclu avec le code client qui utilise l’interface.
L'implémentation d'un module de code est le code interne qui fait fonctionner le module. Souvent, vous pouvez implémenter une interface particulière de plusieurs manières différentes et même modifier l’implémentation sans que le code client n’ait connaissance de la modification.
Une interface Java ne doit être utilisée comme interface au sens générique ci-dessus, pour définir le comportement de la classe au profit du code client utilisant la classe, sans spécifier d'implémentation. Ainsi, une interface inclut des signatures de méthode - les noms, les types de retour et les listes d'arguments - pour les méthodes appelées par le code client, et doit en principe disposer de suffisamment de Javadoc pour chaque méthode décrivant son travail. La raison la plus convaincante de l’utilisation d’une interface est si vous prévoyez d’avoir plusieurs implémentations différentes de l’interface, éventuellement en choisissant une implémentation en fonction de la configuration de déploiement.
Une classe abstraite Java, en revanche, fournit une implémentation partielle de la classe, plutôt que de spécifier une interface. Il doit être utilisé lorsque plusieurs classes partagent du code, mais lorsque les sous-classes doivent également fournir une partie de la mise en œuvre. Cela permet au code partagé d'apparaître à un seul endroit - la classe abstraite - tout en précisant que certaines parties de l'implémentation ne sont pas présentes dans la classe abstraite et devraient être fournies par des sous-classes.
votre réponse est exacte mais l'intervieweur a besoin que vous vous différenciiez en fonction de la perspective du génie logiciel et non en fonction des détails de Java.
Mots simples:
Une interface est comme l'interface d'un magasin, tout ce qui y est présenté doit être présent dans le magasin. Par conséquent, toute méthode de l'interface doit y être implémentée dans la classe concrète. Maintenant que se passe-t-il si certaines classes partagent des méthodes exactes et varient dans d'autres. Supposons que l'interface concerne un magasin qui contient deux choses et supposons que nous ayons deux magasins qui contiennent du matériel de sport mais dont l'un a des vêtements en plus et l'autre des chaussures en plus. Vous créez donc un cours abstrait pour Sport qui implémente la méthode Sports et laisse l’autre méthode non implémentée. La classe abstraite signifie ici que ce magasin n'existe pas en soi, mais qu'il est la base des autres classes/magasins. De cette façon, vous organisez le code, en évitant les erreurs de réplication du code, en unifiant le code et en garantissant sa réutilisation par une autre classe.