Java Language
अपरिवर्तनीय वस्तुएँ
खोज…
टिप्पणियों
अपरिवर्तनीय वस्तुओं की निश्चित स्थिति (कोई बसने वाली) नहीं है, इसलिए सभी राज्य को ऑब्जेक्ट निर्माण समय पर जाना जाना चाहिए।
हालांकि तकनीकी रूप से आवश्यक नहीं है, सभी क्षेत्रों को final
बनाने के लिए यह सबसे अच्छा अभ्यास है। यह अपरिवर्तनीय वर्ग थ्रेड-सेफ़ (cf. जावा कॉनक्यूरिटी इन प्रैक्टिस, 3.4.1) कर देगा।
उदाहरण कई पैटर्न दिखाते हैं जो इसे प्राप्त करने में सहायता कर सकते हैं।
रक्षात्मक नकल का उपयोग करके एक प्रकार का अपरिवर्तनीय संस्करण बनाना।
जावा में कुछ बुनियादी प्रकार और कक्षाएं मौलिक रूप से परस्पर हैं। उदाहरण के लिए, सभी सरणी प्रकार परस्पर हैं, और इसलिए java.util.Data
जैसी कक्षाएं हैं। यह उन स्थितियों में अजीब हो सकता है जहां एक अपरिवर्तनीय प्रकार अनिवार्य है।
इससे निपटने का एक तरीका है, म्यूटेबल टाइप के लिए एक अपरिवर्तनीय आवरण बनाना। पूर्णांक की एक सरणी के लिए यहां एक सरल आवरण है
public class ImmutableIntArray {
private final int[] array;
public ImmutableIntArray(int[] array) {
this.array = array.clone();
}
public int[] getValue() {
return this.clone();
}
}
यह वर्ग किसी भी कोड से उत्परिवर्तनीय स्थिति ( int[]
) को अलग करने के लिए रक्षात्मक प्रतिलिपि का उपयोग करके काम करता है जो इसे उत्परिवर्तित कर सकता है:
पैरामीटर सरणी की एक अलग प्रतिलिपि बनाने के लिए निर्माता
clone()
का उपयोग करता है। यदि कंस्ट्रक्टर के कॉलर ने बाद में पैरामीटर सरणी को बदल दिया, तो यहImmutableIntArray
की स्थिति को प्रभावित नहीं करेगा।getValue()
विधि भीclone()
का उपयोग उस सरणी को बनाने के लिए करती है जो वापस आ गई है। यदि कॉलर परिणाम सरणी को बदलना चाहते थे, तो यहImmutableIntArray
की स्थिति को प्रभावित नहीं करेगा।
हम लिपटे हुए सरणी पर केवल-पढ़ने के लिए ऑपरेशन करने के लिए ImmutableIntArray
विधियाँ भी जोड़ सकते हैं; उदाहरण के लिए, इसकी लंबाई प्राप्त करें, किसी विशेष इंडेक्स पर मान प्राप्त करें, और इसी तरह।
ध्यान दें कि एक अपरिवर्तनीय आवरण प्रकार इस प्रकार लागू किया गया है जो मूल प्रकार के साथ संगत नहीं है। आप बाद के लिए पूर्व को केवल स्थानापन्न नहीं कर सकते।
एक अपरिवर्तनीय वर्ग के लिए नुस्खा
अपरिवर्तनीय वस्तु एक ऐसी वस्तु है जिसकी स्थिति को बदला नहीं जा सकता है। एक अपरिवर्तनीय वर्ग एक ऐसा वर्ग है जिसके उदाहरण डिजाइन, और कार्यान्वयन से अपरिवर्तनीय हैं। जावा वर्ग जिसे आमतौर पर अपरिवर्तनीयता के एक उदाहरण के रूप में प्रस्तुत किया जाता है वह है java.lang.String ।
निम्नलिखित एक रूढ़िवादी उदाहरण है:
public final class Person {
private final String name;
private final String ssn; // (SSN == social security number)
public Person(String name, String ssn) {
this.name = name;
this.ssn = ssn;
}
public String getName() {
return name;
}
public String getSSN() {
return ssn;
}
}
इस पर एक भिन्नता कंस्ट्रक्टर को private
घोषित करने और इसके बजाय एक public static
कारखाना विधि प्रदान करने के लिए है।
एक अपरिवर्तनीय वर्ग के लिए मानक नुस्खा इस प्रकार है:
- सभी संपत्तियों को कंस्ट्रक्टर (फैक्ट्री) या फैक्ट्री मेथड में सेट किया जाना चाहिए।
- कोई बसावट नहीं होनी चाहिए।
- यदि इंटरफ़ेस संगतता कारणों के लिए बसने को शामिल करना आवश्यक है, तो उन्हें या तो कुछ भी नहीं करना चाहिए या अपवाद नहीं फेंकना चाहिए।
- सभी संपत्तियों को
private
औरfinal
घोषित किया जाना चाहिए। - सभी गुणों के लिए जो परस्पर प्रकार के संदर्भ हैं:
- संपत्ति को निर्माणकर्ता द्वारा पारित मूल्य की एक गहरी प्रति के साथ आरंभीकृत किया जाना चाहिए, और
- संपत्ति के प्राप्तकर्ता को संपत्ति के मूल्य की एक गहरी प्रति वापस करनी चाहिए।
- किसी अपरिवर्तनीय वर्ग के एक उत्परिवर्ती उपवर्ग बनाने से रोकने के लिए वर्ग को
final
घोषित किया जाना चाहिए।
कुछ अन्य बातों पर ध्यान दें:
- अपरिहार्यता वस्तु को अशक्त होने से नहीं रोकती है; उदाहरण के लिए
null
String
को सौंपा जा सकता है। - यदि एक अपरिवर्तनीय वर्ग के गुणों को
final
घोषित किया जाता है, तो उदाहरण स्वाभाविक रूप से थ्रेड-सुरक्षित होते हैं। यह अपरिवर्तनीय वर्गों को बहु-थ्रेडेड अनुप्रयोगों को लागू करने के लिए एक अच्छा बिल्डिंग ब्लॉक बनाता है।
विशिष्ट डिजाइन दोष जो एक वर्ग को अपरिवर्तनीय होने से रोकते हैं
कंस्ट्रक्टर में सभी आवश्यक गुणों को सेट किए बिना, कुछ सेटरों का उपयोग करना
public final class Person { // example of a bad immutability
private final String name;
private final String surname;
public Person(String name) {
this.name = name;
}
public String getName() { return name;}
public String getSurname() { return surname;}
public void setSurname(String surname) { this.surname = surname); }
}
यह दिखाना आसान है कि Person
वर्ग अपरिवर्तनीय नहीं है:
Person person = new Person("Joe");
person.setSurname("Average"); // NOT OK, change surname field after creation
इसे ठीक करने के लिए, बस setSurname()
हटाएं setSurname()
और कंस्ट्रक्टर को निम्नानुसार रीफैक्टर करें:
public Person(String name, String surname) {
this.name = name;
this.surname = surname;
}
उदाहरण चर को निजी और अंतिम के रूप में चिह्नित नहीं करना
निम्नलिखित वर्ग पर एक नज़र डालें:
public final class Person {
public String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
निम्नलिखित स्निपेट से पता चलता है कि उपरोक्त वर्ग अपरिवर्तनीय नहीं है:
Person person = new Person("Average Joe");
person.name = "Magic Mike"; // not OK, new name for person after creation
इसे ठीक करने के लिए, बस नाम की संपत्ति को private
और final
रूप में चिह्नित करें।
एक गटर में कक्षा के एक परिवर्तनशील वस्तु को उजागर करना
निम्नलिखित वर्ग पर एक नज़र डालें:
import java.util.List;
import java.util.ArrayList;
public final class Names {
private final List<String> names;
public Names(List<String> names) {
this.names = new ArrayList<String>(names);
}
public List<String> getNames() {
return names;
}
public int size() {
return names.size();
}
}
पहली नज़र में Names
वर्ग अपरिवर्तनीय लगता है, लेकिन यह निम्न कोड शो के रूप में नहीं है:
List<String> namesList = new ArrayList<String>();
namesList.add("Average Joe");
Names names = new Names(namesList);
System.out.println(names.size()); // 1, only containing "Average Joe"
namesList = names.getNames();
namesList.add("Magic Mike");
System.out.println(names.size()); // 2, NOT OK, now names also contains "Magic Mike"
ऐसा इसलिए हुआ क्योंकि getNames()
द्वारा लौटी संदर्भ सूची में बदलाव से Names
की वास्तविक सूची को संशोधित किया जा सकता है।
इसे ठीक करने के लिए, बस बचाव की मुद्रा में प्रतियां बनाकर या तो उस संदर्भ वर्ग के परिवर्तनशील वस्तुओं संदर्भ लौटने से बचने के लिए इस प्रकार है:
public List<String> getNames() {
return new ArrayList<String>(this.names); // copies elements
}
या इस तरह से गेटर्स डिजाइन करके कि केवल अन्य अपरिवर्तनीय वस्तुओं और प्राइमिटिव्स को लौटाया जाता है, निम्नानुसार है:
public String getName(int index) {
return names.get(index);
}
public int size() {
return names.size();
}
ऑब्जेक्ट के साथ कंस्ट्रक्टर को इंजेक्ट करना जो कि अपरिवर्तनीय वर्ग के बाहर संशोधित किया जा सकता है
यह पिछले दोष का एक रूपांतर है। निम्नलिखित वर्ग पर एक नज़र डालें:
import java.util.List;
public final class NewNames {
private final List<String> names;
public Names(List<String> names) {
this.names = names;
}
public String getName(int index) {
return names.get(index);
}
public int size() {
return names.size();
}
}
जैसा कि Names
वर्ग से पहले, NewNames
वर्ग भी पहली नजर में अपरिवर्तनीय लगता है, लेकिन ऐसा नहीं है, वास्तव में निम्नलिखित स्निपेट इसके विपरीत साबित होते हैं:
List<String> namesList = new ArrayList<String>();
namesList.add("Average Joe");
NewNames names = new NewNames(namesList);
System.out.println(names.size()); // 1, only containing "Average Joe"
namesList.add("Magic Mike");
System.out.println(names.size()); // 2, NOT OK, now names also contains "Magic Mike"
इसे ठीक करने के लिए, पिछले दोष के रूप में, बस इसे अपरिवर्तनीय वर्ग को सीधे असाइन किए बिना ऑब्जेक्ट की रक्षात्मक प्रतियां बनाएं, अर्थात निर्माणकर्ता को निम्नानुसार बदला जा सकता है:
public Names(List<String> names) {
this.names = new ArrayList<String>(names);
}
कक्षा के तरीकों को ओवरराइड होने देना
निम्नलिखित वर्ग पर एक नज़र डालें:
public class Person {
private final String name;
public Person(String name) {
this.name = name;
}
public String getName() { return name;}
}
Person
वर्ग पहली नजर में अपरिवर्तनीय लगता है, लेकिन मान लीजिए कि Person
एक नए उपवर्ग को परिभाषित किया गया है:
public class MutablePerson extends Person {
private String newName;
public MutablePerson(String name) {
super(name);
}
@Override
public String getName() {
return newName;
}
public void setName(String name) {
newName = name;
}
}
अब Person
(im) उत्परिवर्तन का उपयोग नए उपवर्ग का उपयोग करके बहुरूपता के माध्यम से किया जा सकता है:
Person person = new MutablePerson("Average Joe");
System.out.println(person.getName()); prints Average Joe
person.setName("Magic Mike"); // NOT OK, person has now a new name!
System.out.println(person.getName()); // prints Magic Mike
इसे ठीक करने के लिए, या तो वर्ग को final
रूप से चिह्नित करें ताकि इसे विस्तारित नहीं किया जा सके या इसके सभी निर्माणकर्ता को private
घोषित न किया जा सके।