Scala Language
वार, वैल और डेफ
खोज…
टिप्पणियों
के रूप में val
शब्दार्थ स्थिर हैं, वे "यथा-स्थान" प्रारंभ कर रहे हैं चाहे वे कहीं भी कोड में दिखाई देते हैं। अमूर्त वर्गों और लक्षणों में उपयोग किए जाने पर यह आश्चर्यजनक और अवांछनीय व्यवहार पैदा कर सकता है।
उदाहरण के लिए, PlusOne
लें कि हम PlusOne
नामक एक विशेषता बनाना चाहते हैं जो एक लिपटे Int
पर एक वृद्धि ऑपरेशन को परिभाषित करता है। चूंकि Int
एस अपरिवर्तनीय हैं, इसलिए मूल्य प्लस एक को आरंभीकरण पर जाना जाता है और बाद में इसे कभी नहीं बदला जाएगा, इसलिए शब्दार्थ यह एक val
। हालांकि, इसे इस तरह परिभाषित करने से अप्रत्याशित परिणाम प्राप्त होगा।
trait PlusOne {
val i:Int
val incr = i + 1
}
class IntWrapper(val i: Int) extends PlusOne
कोई फर्क नहीं पड़ता मूल्य i
आप का निर्माण IntWrapper
साथ, बुला .incr
लौटे वस्तु पर हमेशा वापस आ जाएगी 1. इसका कारण यह है वैल है incr
विस्तार कक्षा से पहले, विशेषता में आरंभ नहीं हो जाता है, और उस समय i
केवल का डिफ़ॉल्ट मान है 0
। (अन्य स्थितियों में, इसे Nil
, null
या इसी तरह के डिफ़ॉल्ट के साथ आबाद किया जा सकता है।)
सामान्य नियम, फिर, किसी भी मूल्य पर val
का उपयोग करने से बचना है जो एक अमूर्त क्षेत्र पर निर्भर करता है। इसके बजाय, lazy val
उपयोग करें, जो तब तक मूल्यांकन नहीं करता है जब तक इसकी आवश्यकता नहीं है, या def
, जो हर बार मूल्यांकन करने के बाद इसे कहा जाता है। ध्यान दें कि यदि lazy val
को आरंभीकरण पूरा होने से पहले एक val
द्वारा मूल्यांकन करने के लिए मजबूर किया जाता है, तो वही त्रुटि होगी।
एक फिडेल (स्काला-जेएस में लिखा गया है, लेकिन एक ही व्यवहार लागू होता है) यहां पाया जा सकता है।
वार, वैल और डेफ
वर
एक var
एक संदर्भ चर है, जावा जैसी भाषाओं में चर के समान है। विभिन्न वस्तुओं को एक var
स्वतंत्र रूप से सौंपा जा सकता है, इसलिए जब तक कि दी गई वस्तु में एक ही प्रकार होता है जो कि var
साथ घोषित किया गया था:
scala> var x = 1
x: Int = 1
scala> x = 2
x: Int = 2
scala> x = "foo bar"
<console>:12: error: type mismatch;
found : String("foo bar")
required: Int
x = "foo bar"
^
के प्रकार के ऊपर के उदाहरण में नोट var
पहले मान काम दिया संकलक द्वारा अनुमान लगाया गया था।
वैल
एक val
एक निरंतर संदर्भ है। इस प्रकार, एक नई वस्तु को एक val
को नहीं सौंपा जा सकता है जिसे पहले से ही सौंपा गया है।
scala> val y = 1
y: Int = 1
scala> y = 2
<console>:12: error: reassignment to val
y = 2
^
हालांकि, उद्देश्य यह है कि एक val
अंक पर स्थिर नहीं है। उस वस्तु को संशोधित किया जा सकता है:
scala> val arr = new Array[Int](2)
arr: Array[Int] = Array(0, 0)
scala> arr(0) = 1
scala> arr
res1: Array[Int] = Array(1, 0)
डीईएफ़
एक def
एक विधि को परिभाषित करता है। एक विधि को फिर से नहीं सौंपा जा सकता है।
scala> def z = 1
z: Int
scala> z = 2
<console>:12: error: value z_= is not a member of object $iw
z = 2
^
उपरोक्त उदाहरणों में, val y
और def z
समान मान लौटाते हैं। हालांकि, किसी def
का मूल्यांकन तब किया जाता है जब इसे कहा जाता है , जबकि किसी val
या var
का मूल्यांकन तब किया जाता है जब इसे असाइन किया जाता है । इसके परिणामस्वरूप भिन्न व्यवहार हो सकते हैं जब परिभाषा के दुष्प्रभाव होते हैं:
scala> val a = {println("Hi"); 1}
Hi
a: Int = 1
scala> def b = {println("Hi"); 1}
b: Int
scala> a + 1
res2: Int = 2
scala> b + 1
Hi
res3: Int = 2
कार्य
क्योंकि फ़ंक्शन मान हैं, उन्हें val
/ var
/ def
s में असाइन किया जा सकता है। बाकी सब उसी तरीके से काम करते हैं जैसे ऊपर:
scala> val x = (x: Int) => s"value=$x"
x: Int => String = <function1>
scala> var y = (x: Int) => s"value=$x"
y: Int => String = <function1>
scala> def z = (x: Int) => s"value=$x"
z: Int => String
scala> x(1)
res0: String = value=1
scala> y(2)
res1: String = value=2
scala> z(3)
res2: String = value=3
आलसी घाटी
lazy val
एक भाषा की विशेषता है जहां पहली बार पहुंचने तक किसी val
के आरंभ में देरी होती है। उस बिंदु के बाद, यह एक नियमित val
तरह काम करता है।
इसका उपयोग करने के लिए val
से पहले lazy
कीवर्ड जोड़ें। उदाहरण के लिए, REPL का उपयोग कर:
scala> lazy val foo = {
| println("Initializing")
| "my foo value"
| }
foo: String = <lazy>
scala> val bar = {
| println("Initializing bar")
| "my bar value"
| }
Initializing bar
bar: String = my bar value
scala> foo
Initializing
res3: String = my foo value
scala> bar
res4: String = my bar value
scala> foo
res5: String = my foo value
यह उदाहरण निष्पादन आदेश को प्रदर्शित करता है। जब lazy val
घोषित किया जाता है, तो वह सब जो foo
वैल्यू में सेव होता है, एक आलसी फंक्शन कॉल है, जिसका मूल्यांकन अभी तक नहीं किया गया है। जब नियमित val
सेट किया जाता है, तो हम println
कॉल निष्पादित करते हैं और मूल्य को bar
को सौंपा जाता है। हम evalute जब foo
पहली बार हम देखते हैं println
पर अमल - लेकिन यह दूसरी बार नहीं मूल्यांकन किया जाता है जब। इसी तरह, जब bar
का मूल्यांकन किया जाता है तो हमें println
एक्जीक्यूट दिखाई नहीं देता है - केवल जब यह घोषित किया जाता है।
जब 'आलसी' का उपयोग करने के लिए
प्रारंभ कम्प्यूटेशनल रूप से महंगा है और
val
का उपयोग दुर्लभ है।lazy val tiresomeValue = {(1 to 1000000).filter(x => x % 113 == 0).sum} if (scala.util.Random.nextInt > 1000) { println(tiresomeValue) }
tiresomeValue
गणना के लिए एक लंबा समय लगता है, और यह हमेशा उपयोग नहीं किया जाता है। इसेlazy val
बनाने से अनावश्यक संगणना बचती है।चक्रीय निर्भरता को हल करना
आइए एक उदाहरण को दो वस्तुओं के साथ देखें जिन्हें तात्कालिकता के दौरान एक ही समय में घोषित करने की आवश्यकता है:
object comicBook { def main(args:Array[String]): Unit = { gotham.hero.talk() gotham.villain.talk() } } class Superhero(val name: String) { lazy val toLockUp = gotham.villain def talk(): Unit = { println(s"I won't let you win ${toLockUp.name}!") } } class Supervillain(val name: String) { lazy val toKill = gotham.hero def talk(): Unit = { println(s"Let me loosen up Gotham a little bit ${toKill.name}!") } } object gotham { val hero: Superhero = new Superhero("Batman") val villain: Supervillain = new Supervillain("Joker") }
कीवर्ड के बिना
lazy
, संबंधित ऑब्जेक्ट किसी ऑब्जेक्ट के सदस्य नहीं हो सकते। इस तरह के कार्यक्रम के निष्पादन सेjava.lang.NullPointerException
परिणाम होगा।lazy
का उपयोग करके, संदर्भ को एक आरंभिक मूल्य के डर के बिना, आरंभीकृत होने से पहले सौंपा जा सकता है।
ओवरलोडिंग डिफ
आप एक ओवरलोड कर सकते हैं def
यदि हस्ताक्षर अलग है:
def printValue(x: Int) {
println(s"My value is an integer equal to $x")
}
def printValue(x: String) {
println(s"My value is a string equal to '$x'")
}
printValue(1) // prints "My value is an integer equal to 1"
printValue("1") // prints "My value is a string equal to '1'"
यह वही कार्य करता है, जो कक्षाओं, लक्षणों, वस्तुओं या वस्तुओं के अंदर होता है।
नामित पैरामीटर्स
जब कोई def
, तो पैरामीटर को नाम से स्पष्ट रूप से असाइन किया जा सकता है। ऐसा करने का मतलब है कि उन्हें सही तरीके से ऑर्डर करने की आवश्यकता नहीं है। उदाहरण के लिए, printUs()
को इस printUs()
परिभाषित करें:
// print out the three arguments in order.
def printUs(one: String, two: String, three: String) =
println(s"$one, $two, $three")
अब इसे इन तरीकों से (दूसरों के बीच) कहा जा सकता है:
printUs("one", "two", "three")
printUs(one="one", two="two", three="three")
printUs("one", two="two", three="three")
printUs(three="three", one="one", two="two")
इससे सभी मामलों में one, two, three
प्रिंट हो रहे हैं।
यदि सभी तर्कों का नाम नहीं दिया जाता है, तो पहली दलीलें क्रम से मेल खाती हैं। कोई भी स्थितिगत (गैर-नामित) तर्क किसी नामित का अनुसरण नहीं कर सकता है:
printUs("one", two="two", three="three") // prints 'one, two, three'
printUs(two="two", three="three", "one") // fails to compile: 'positional after named argument'