Imaginez que j'ai cette classe:
public class Test
{
private String[] arr = new String[]{"1","2"};
public String[] getArr()
{
return arr;
}
}
Maintenant, j'ai une autre classe qui utilise la classe ci-dessus:
Test test = new Test();
test.getArr()[0] ="some value!"; //!!!
Voilà donc le problème: j'ai accédé à un champ privé d'une classe de l'extérieur! Comment puis-je éviter cela? Je veux dire comment puis-je rendre ce tableau immuable? Cela signifie-t-il qu'avec chaque méthode getter, vous pouvez progresser pour accéder au domaine privé? (Je ne veux pas de bibliothèques telles que Guava. J'ai juste besoin de savoir comment faire cela).
Vous devez retourner un copie de votre tableau.
public String[] getArr() {
return arr == null ? null : Arrays.copyOf(arr, arr.length);
}
Si vous pouvez utiliser une liste au lieu d'un tableau, Collections fournit une liste non modifiable :
public List<String> getList() {
return Collections.unmodifiableList(list);
}
Le modificateur private
protège uniquement le champ lui-même contre l'accès à partir d'autres classes, mais pas les références d'objet par ce champ. Si vous avez besoin de protéger un objet référencé, ne le donnez pas. Changement
public String [] getArr ()
{
return arr;
}
à:
public String [] getArr ()
{
return arr.clone ();
}
ou pour
public int getArrLength ()
{
return arr.length;
}
public String getArrElementAt (int index)
{
return arr [index];
}
Le Collections.unmodifiableList
A déjà été mentionné - la Arrays.asList()
étrangement pas! Ma solution serait également d'utiliser la liste de l'extérieur et d'envelopper le tableau comme suit:
String[] arr = new String[]{"1", "2"};
public List<String> getList() {
return Collections.unmodifiableList(Arrays.asList(arr));
}
Le problème avec la copie du tableau est: si vous le faites chaque fois que vous accédez au code et que le tableau est grand, vous créerez certainement beaucoup de travail pour le garbage collector. La copie est donc une approche simple mais vraiment mauvaise - je dirais "pas cher", mais cher en mémoire! Surtout lorsque vous avez plus de 2 éléments.
Si vous regardez le code source de Arrays.asList
Et Collections.unmodifiableList
Il n'y a en fait pas grand-chose créé. Le premier encapsule simplement le tableau sans le copier, le second encapsule simplement la liste, rendant les modifications indisponibles.
Vous pouvez également utiliser ImmutableList
qui devrait être meilleur que le standard unmodifiableList
. La classe fait partie des bibliothèques Guava créées par Google.
Voici la description:
Contrairement à Collections.unmodifiableList (Java.util.List), qui est une vue d'une collection distincte qui peut encore changer, une instance de ImmutableList contient ses propres données privées et ne changera jamais
Voici un exemple simple de son utilisation:
public class Test
{
private String[] arr = new String[]{"1","2"};
public ImmutableList<String> getArr()
{
return ImmutableList.copyOf(arr);
}
}
à ce point de vue, vous devez utiliser la copie de la matrice système:
public String[] getArr() {
if (arr != null) {
String[] arrcpy = new String[arr.length];
System.arraycopy(arr, 0, arrcpy, 0, arr.length);
return arrcpy;
} else
return null;
}
}
Vous pouvez renvoyer une copie des données. L'appelant qui choisit de modifier les données ne modifiera que la copie
public class Test {
private static String[] arr = new String[] { "1", "2" };
public String[] getArr() {
String[] b = new String[arr.length];
System.arraycopy(arr, 0, b, 0, arr.length);
return b;
}
}
Le nœud du problème est que vous renvoyez un pointeur vers un objet mutable. Oops. Soit vous rendez l'objet immuable (la solution de liste non modifiable), soit vous renvoyez une copie de l'objet.
D'une manière générale, la finalité des objets ne protège pas les objets d'être modifiés s'ils sont mutables. Ces deux problèmes sont "embrasser des cousins".
Renvoyer une liste non modifiable est une bonne idée. Mais une liste rendue non modifiable lors de l'appel à la méthode getter peut toujours être modifiée par la classe ou des classes dérivées de la classe.
Au lieu de cela, vous devez indiquer clairement à quiconque étend la classe que la liste ne doit pas être modifiée.
Ainsi, dans votre exemple, cela pourrait conduire au code suivant:
import Java.util.Arrays;
import Java.util.Collections;
import Java.util.List;
public class Test {
public static final List<String> STRINGS =
Collections.unmodifiableList(
Arrays.asList("1", "2"));
public final List<String> getStrings() {
return STRINGS;
}
}
Dans l'exemple ci-dessus, j'ai rendu public le champ STRINGS
, en principe, vous pouvez supprimer l'appel de méthode, car les valeurs sont déjà connues.
Vous pouvez également affecter les chaînes à un private final List<String>
champ rendu non modifiable lors de la construction de l'instance de classe. L'utilisation d'une constante ou d'arguments d'instanciation (du constructeur) dépend de la conception de la classe.
import Java.util.Arrays;
import Java.util.Collections;
import Java.util.List;
public class Test {
private final List<String> strings;
public Test(final String ... strings) {
this.strings = Collections.unmodifiableList(Arrays
.asList(strings));
}
public final List<String> getStrings() {
return strings;
}
}
Oui, vous devez renvoyer une copie du tableau:
public String[] getArr()
{
return Arrays.copyOf(arr);
}