Je souhaite savoir s'il existe un moyen d'imposer à la classe implémenteur l'obligation de déclarer les descripteurs/primitives d'objets comme ils le font avec les méthodes .
public interface Rectangle {
int height = 0;
int width = 0;
public int getHeight();
public int getWidth();
public void setHeight(int height);
public void setWidth(int width);
}
public class Tile implements Rectangle{
@Override
public int getHeight() {
return 0;
}
@Override
public int getWidth() {
return 0;
}
@Override
public void setHeight(int height) {
}
@Override
public void setWidth(int width) {
}
}
Dans la méthode ci-dessus, comment pouvons-nous obliger la classe Tile à déclarer les attributs de hauteur et de largeur à l'aide de l'interface? Pour une raison quelconque, je souhaite le faire avec une interface uniquement!
J'ai d'abord pensé l'utiliser avec l'héritage… .. Mais c'est que je dois traiter avec 3 classes.!
class Tile extends JLabel implements Rectangle {}
travaillerait.!
mais
class Tile extends JLabel extends Rectangle {}
woud not!
Le point d'une interface est de spécifier l'API publique. Une interface n'a pas d'état. Toutes les variables que vous créez sont réellement des constantes (soyez donc prudent lorsque vous créez des objets mutables dans des interfaces).
Fondamentalement, une interface indique ici toutes les méthodes qu'une classe qui l'implémente doit prendre en charge. Il aurait probablement été préférable que les créateurs de Java n'autorisent pas les constantes dans les interfaces, mais trop tard pour s'en débarrasser maintenant (et dans certains cas, les constantes sont sensibles dans les interfaces).
Comme vous spécifiez simplement les méthodes à implémenter, il n’ya pas d’idée d’état (pas de variables d’instance). Si vous voulez exiger que chaque classe ait une certaine variable, vous devez utiliser une classe abstraite.
Enfin, vous ne devriez généralement pas utiliser de variables publiques. Par conséquent, l'idée de placer des variables dans une interface est une mauvaise idée.
Réponse courte - vous ne pouvez pas faire ce que vous voulez parce que c'est "faux" en Java.
Modifier:
class Tile
implements Rectangle
{
private int height;
private int width;
@Override
public int getHeight() {
return height;
}
@Override
public int getWidth() {
return width;
}
@Override
public void setHeight(int h) {
height = h;
}
@Override
public void setWidth(int w) {
width = w;
}
}
une version alternative serait:
abstract class AbstractRectangle
implements Rectangle
{
private int height;
private int width;
@Override
public int getHeight() {
return height;
}
@Override
public int getWidth() {
return width;
}
@Override
public void setHeight(int h) {
height = h;
}
@Override
public void setWidth(int w) {
width = w;
}
}
class Tile
extends AbstractRectangle
{
}
Vous ne pouvez le faire qu'avec une classe abstraite, pas avec une interface.
Déclarez Rectangle
en tant que abstract class
au lieu de interface
et déclarez les méthodes qui doivent être implémentées par la sous-classe en tant que public abstract
. Ensuite, la classe Tile
étend la classe Rectangle
et doit implémenter les méthodes abstraites de Rectangle
.
Les interfaces ne peuvent pas exigent que les variables d'instance soient définies - uniquement des méthodes.
( Les variables peuvent être définies dans les interfaces , mais elles ne se comportent pas comme prévu: elles sont traitées comme final static
.)
Bonne codage.
Java 8 a introduit méthodes par défaut pour les interfaces à l'aide desquelles vous pouvez corps aux méthodes. Selon les POO, les interfaces doivent faire office de contrat entre deux systèmes/parties.
Mais j'ai quand même trouvé un moyen d'obtenir des propriétés de stockage dans l'interface. J'admets que c'est une mise en œuvre un peu moche.
import Java.util.Map;
import Java.util.WeakHashMap;
interface Rectangle
{
class Storage
{
private static final Map<Rectangle, Integer> heightMap = new WeakHashMap<>();
private static final Map<Rectangle, Integer> widthMap = new WeakHashMap<>();
}
default public int getHeight()
{
return Storage.heightMap.get(this);
}
default public int getWidth()
{
return Storage.widthMap.get(this);
}
default public void setHeight(int height)
{
Storage.heightMap.put(this, height);
}
default public void setWidth(int width)
{
Storage.widthMap.put(this, width);
}
}
Cette interface est moche. Pour stocker une propriété simple, il fallait deux hashmaps et chaque hashmap crée par défaut 16 entrées. En outre, lorsqu'un objet réel est déréférencé, la machine virtuelle Java doit également supprimer cette référence faible.
En Java, vous ne pouvez pas. L'interface concerne les méthodes et la signature, mais pas l'état interne d'un objet, c'est une question d'implémentation. Et cela a également un sens - je veux dire, simplement parce que certains attributs existent, cela ne signifie pas qu’ils doivent être utilisés par la classe d’implémentation. getHeight pourrait en fait pointer sur la variable width (en supposant que l'implémenteur soit un sadique).
(Remarque: ce n'est pas le cas de tous les langages. ActionScript permet la déclaration de pseudo-attributs, et je pense que C # aussi.)
Les champs dans les interfaces sont implicitement public static final
. (Les méthodes sont également implicitement publiques, vous pouvez donc supprimer le mot clé public
.) Même si vous utilisez une classe abstraite au lieu d'une interface, je suggère fortement de rendre tous les éléments non constants (public static final
d'une référence d'objet immuable ou immuable) private
. Plus généralement, "préférez la composition à l'héritage" - une Tile
n'est pas une Rectangle
(bien sûr, vous pouvez jouer à des jeux de mots avec "est-a" et "a-a").
Tom a dit quelque chose d'important:
si vous utilisez le concept has-a, vous évitez le problème.
En effet, si, au lieu d'utiliser extend et implements , vous définissez deux attributs, l'un de type rectangle, l'un de type JLabel
dans votre classe Tile
, vous pouvez définir une Rectangle
comme étant une interface ou une classe. .
De plus, j'encouragerais normalement l'utilisation d'interfaces en liaison avec has-a, mais j'imagine que ce serait excessif dans votre cas. Cependant, vous êtes le seul à pouvoir décider de ce point (compromis/flexibilité).