2010-01-05 7 views
8

Je commence par Java et j'apprends des setters, des getters et de l'encapsulation. J'ai un programme très simple, deux classes:Comment encapsuler un tableau en Java

  • Container a un tableau int privé (numArray) avec son getter setter &.

  • Main crée un objet Container et l'utilise dans la méthode totalArray.


public class Container { 
    private int numArray[]= {0,0,0}; 
    public int[] getNumArray() { 
     return numArray; 
    } 
    public void setNumArray(int index, int value){ 
     numArray[index] = value; 
    }  
} 

public class Main { 
    public static void main(String[] args) { 
     Container conte = new Container(); 
     System.out.println(totalArray(conte.getNumArray())); 
     conte.getNumArray()[2]++; 
     System.out.println(totalArray(conte.getNumArray())); 
    } 
    private static int totalArray (int v[]){ 
     int total=0; 
     for (int conta =0; conta<v.length;conta++){ 
      total+=v[conta]; 
     } 
     return total; 
    } 
} 

Problème: Je peux changer le tableau int privé à travers le getter, je sais que ce que getNumArray renvoie une référence à numArray, pas le tableau lui-même. Si j'étais intéressé par un seul élément du tableau, je ferais un getter avec une valeur d'index, mais je veux tout le tableau pour la méthode totalArray.

Comment puis-je empêcher numArray d'être modifié en dehors de sa classe?

Répondre

8

Tout ce que vous pouvez faire pour empêcher les gens de modifier votre tableau est d'en fournir une copie dans le getter.

public int[] getArray() { 
    return Arrays.copyOf(numArray, numArray.length); 
} 

De cette façon, d'autres méthodes peuvent changer leur propre copie du tableau, mais quand ils appellent le getter à nouveau, ils obtiennent la version originale, inchangée. Seul le setNumArray() que vous fournissez peut réellement modifier votre tableau interne. Sinon, si vous voulez bloquer complètement le conteneur, vous devez déposer des tableaux et utiliser un objet immuable. Les bibliothèques Some fournissent des listes immuables ou utilisent Collections.unmodifiableList.

+0

'Some' comme dans la bibliothèque Java standard. –

+1

@Pete: oui mais pas seulement. – glmxndr

3

Généralement, vous regardez l'interface que vous essayez de fournir aux appelants de votre classe.

méthodes comme:

void addItem(Object item); 
Object getItem(int index); 
int getSize(); 

sont le genre de choses que vous fournissez dans votre classe de conteneur. Ils interagissent ensuite avec le tableau privé au nom de l'appelant.

Si vous souhaitez renvoyer l'ensemble du tableau sans autoriser les modifications, vous pouvez dans le getter copier le tableau dans un nouveau tableau et renvoyer la copie. Sinon, si vous utilisez les classes de collection Java au lieu du tableau primitif, elles fournissent une méthode non modifiable (par exemple, Collections.unmodifiableList(myList)) qui fournit un wrapper en lecture seule autour de la collection.

+1

Veuillez corriger les noms de vos méthodes. Les méthodes Java commencent par des lettres minuscules et Object est une classe (majuscule). –

+0

Merci, c'est ce qui arrive quand vous passez une journée à regarder C# ...;) – Paolo

+1

+1: Encapsulez votre tableau en ** n'exposant pas du tout l'existence du tableau **! Rendre le conteneur avoir une interface riche qui représente les fonctions réelles sur cette classe, plutôt que de simplement renvoyer ses membres de données internes aux appelants à faire avec eux comme ils le souhaitent. –

6

Si vous voulez retourner un tableau, vous clonez:

public int[] getArray() { 
     return (int[]) numArray.clone(); 
    } 

Dans une API publique, vous devez être sûr de documenter que clairement aux appelants (vraiment de toute façon, si elles obtiennent un tableau cela changera l'état de la classe ou non - ils doivent savoir).

0

L'encapsulation est le processus de masquage de l'implémentation.Si la collection stocke ses données dans un tableau ou non est un détail d'implémentation; Si elle était encapsulée, vous voudriez pouvoir la changer en un autre type de stockage. Le fait même que vous exposiez l'état (ou l'état dérivé) en tant que getters et setters rompt l'encapsulation et implique que vous implémentez un type de données abstrait plutôt qu'une vraie classe orientée objet. Par exemple, un ArrayList est un type de données qui ne représente aucune véritable encapsulation de comportement dans une application. Que ce soit ce que vous voulez dépend de comment et où le type doit être utilisé. J'aurais tendance soit à Container mettre en œuvre Iterable<Integer> pour l'interation externe s'il s'agit simplement d'un type de données de conteneur, ou fournir une méthode d'itérateur interne à laquelle vous passez un visiteur s'il a voulu constituer une classe encapsulée. S'il s'agit d'un type de données abstrait, envisagez fortement d'utiliser ceux qui sont intégrés, tels que int[] ou List<Integer> à la place.

0

Il existe encore un autre moyen, qui ne fait pas de copie du tableau, mais présente d'autres inconvénients. Je voudrais l'utiliser pour les très grandes baies:

private A[] items; 

public List<A> getItems() { 
    return Collections.unmodifiableList(Arrays.asList(items)); 
} 
0

Je voudrais suggérer une approche différente de toutes les réponses que j'ai vues ici. C'est pratique quand vous pensez à l'encapsulation de penser aussi à la règle "Ne demandez pas à un objet de récupérer ses données, demandez à un objet d'opérer sur ses données pour vous".

Vous ne m'avez pas donné une utilisation pour numArray, je vais prétendre que votre objectif était de créer un "vecteur" numérique à partir de mathématiques (pas un vecteur Java) à des fins d'exemple.

Vous créez donc une classe NumericVector qui contient un tableau de doubles. Votre NumericVector aurait des méthodes comme multiplyByScalar(double scalar) et addVector (NumericVector secondVector) pour ajouter des éléments.

Votre baie interne est complètement encapsulée - elle ne s'échappe jamais. Toute opération effectuée sur elle est effectuée dans votre classe NumericVector via ces "méthodes métier". Comment l'affichez-vous après l'avoir utilisé? Avez-NumericVector écraser NumericVector.toString() afin qu'il imprime correctement, ou si vous avez une interface graphique, écrire une classe "Controller" pour transférer des données de votre modèle (NumbericVector) à votre vue (GUI). Cela peut nécessiter un moyen de diffuser des éléments à partir de votre NumericVector.

Ceci indique également quelques choses à éviter: Ne créez pas automatiquement des setters et getters, ils cassent votre encapsulation. Vous avez souvent besoin de getters mais, comme d'autres l'ont dit, vous devriez faire en sorte que votre getter retourne une version immuable du tableau. Essayez aussi de rendre vos classes immuables dans la mesure du possible. Cette méthode que j'ai mentionné précédemment numericVector.addVector(NumericVector secondVector) ne devrait probablement pas modifier numericVector mais retourner un nouveau NumericVector avec la solution. Le cas où ceci (et OO en général) échoue souvent est des bibliothèques - quand vous vraiment voulez ajouter un peu de fonctionnalité à votre tableau mais le laissez toujours comme un tableau d'usage général. Dans ce cas Java ne s'occupe généralement pas de l'encapsulation, il ajoute simplement une méthode/classe d'aide qui peut faire des choses à la collection/tableau (regardez l'objet "Arrays" pour un tas de bons exemples).

0

Comment encapsulent un tableau en Java

La question est peut-être assez clair, mais je pense que le point de POO est de concevoir une classe comme une entité qui manipule ce tableau et extrait son propre api.

Si vous persistez retournez alors un clone du tableau /copie défensive est la voie à suivre pour les cas simples .

0

Une mémoire de manière efficace de le faire est ...

package com.eric.stackoverflow.example; 

import java.util.List; 

import com.google.common.collect.ImmutableList; 

public class Container { 
    private int numArray[] = {0, 0, 0}; 
    public int[] getNumArray() { 
     return ImmutableList.copyOf(numArray); 
    } 
    public void setNumArray(int index, int value) { 
     numArray[index] = value; 
    }  
} 

Cela permet à l'ImmutableList pour définir le nombre correct d'éléments de la matrice de support du List et réduit le nombre d'objets intermédiaires qui sont créées .