Object Semntics 6.170 Lecture 2 The objectives of this lecture re to: to help you become fmilir with the bsic runtime mechnism common to ll object-oriented lnguges (but with prticulr focus on Jv): vribles, object references, ssignments, mutbility, nd so on; to introduce digrmmtic nottion, object digrms for describing snpshots (tht is, prticulr configurtions of objects in the hep); 1 to mke you wre, in pssing, of some tricky issues tht we ll return to lter in more detil, nd which turn out to be of fundmentl importnce: equlity, rep invrints nd exposure. When you ve completed this mteril, you should hve solid grsp of wht hppens when Jv code executes, so tht you cn predict wht some code will do without running it. 1 Vribles, References nd Objects Some types of objects cn be creted with literls. Wht hppens when you run this? 1. String = "zeeb"; 2. String b =.touppercse (); 3. System.out.println (b); It prints ZEEB. Sttement 2 is cll to the method touppercse. The method hs receiver. The cll results in the cretion of fresh string tht is then bound to the vrible b. We cn drw the result of the first two sttements s n object digrm, showing tht is reference to n object of type String (nd not the string itself, or slot tht holds it); similrly for b. b "zeeb" "ZEEB" 1 Wht does this do? 1. String = "zeeb"; 2..toUpperCse (); 3. System.out.println (); Lter, we will introduce object models tht re fr more useful nd which describe sets of snpshots. 1
It prints zeeb. Sttement 2 cretes fresh string tht gets thrown wy since it is bound to no vrible. The string object referenced by is not chnged: strings re immutble. Wht bout this? String = "zeeb"; =.touppercse (); System.out.println (); Agin, no object chnges. But is mde to refer to the new object by the ssignment in Sttement 2, so the result is ZEEB. "zeeb" "ZEEB" 2 Alising, Mutbility nd Reference Equlity Jv provides vriety of collections s prt of its stndrd librry. A vector is like n rry, but it cn grow nd shrink dynmiclly. An exmple of using vector: 1. Vector v = new Vector (); 2. Vector k = v; 3. String = "zeeb"; 4. v.dd (); 5. k.dd (.touppercse()); 6. System.out.println (v.lstelement ()); The method lstelement is like the string method touppercse: it hs no effect on the receiver nd returns reference to n object (in this cse, the lst element of the vector v). The method dd, on the other hnd, tkes n rgument the string. Unlike touppercse, the method dd does not return n object, but chnges or muttes its receiver object. Vectors, unlike strings, cn chnge, nd re thus sid to be mutble. The code bove prints ZEEB, since fter Sttement 2 the two vribles k nd v re nmes for the sme vector object: they re sid to be lises. The clls to dd mutte the one vector object, first dding the object for the lower cse string, then the upper cse string. The chnges re visible through both nmes, so in Sttements 4 to 6 we could ctully permute the nmes k nd v without ny chnge in behvior. Here is the object digrm: v k (Vector) elts[0] "zeeb" "ZEEB" elts[1] 2
Alising is pervsive in lnguges like Jv, nd very useful. But it dds lot of complexity. For one thing, it breks the rule tht sttement ffects only the vribles it mentions. Just becuse v isn t mentioned in Sttement 5 doesn t men tht it won t ffect the result of sttement 6 which mentions only v nd not k. How cn we observe the lising more directly? By testing equlity: Vector v = new Vector (); Vector k = v; if (v == k) System.out.println ("sme"); System.out.println ("different"); which results in sme being printed. The built-in == test tells you whether two references re for the sme object, so it s often clled test of reference equlity. Wht does this do? Vector v = new Vector (); Vector k = new Vector (); if (v == k) System.out.println ("sme"); System.out.println ("different"); It prints different, becuse v nd k re distinct objects. It s fundmentl property of constructors tht the objects they return relly re fresh. In fct, the grbge collector cn recycle n object, but only if there is no reference to it still round. This ensures tht even if objects re recycled, we cn never tell. A puzzle: wht does this do? String = "zeeb"; String b = "zeeb"; if ( == b) System.out.println ("sme"); System.out.println ("different"); Strngely, this prints sme, becuse the Jv virtul mchine utomticlly interns string literls: if it cn tell tht two string literls hve the sme sequence of chrcters, it only lloctes one object. You d be right to think this is bit confusing; it s performnce optimiztion. In fct, it s very bd form to test reference equlity of immutble objects, unless you re doing something subtle with memory mngement. Argubly it s design defect of Jv tht you cn even observe whether two immutble objects re the sme or not. So how should you compre two immutble objects? With n equls method. The String clss provides method equls tht tells you whether two strings contin the sme sequence of chrcters or not. This code String = "zeeb"; String b =.touppercse (); 3
if (b.equls ("ZEEB")) System.out.println ("sme chrcters"); System.out.println ("different chrcters"); prints sme chrcters. When we study inheritnce, you ll lern tht every clss utomticlly inherits n equls method, so you might think you don t need to write one. But it s lmost never wht you wnt, so whenever you design clss, one of the first things you ll need to figure out is when two objects of the clss should be considered equl to one nother. Here re some questions: Would you expect tht generlly x == y implies x.equls (y)? Yes, it should. Becuse the equls method cn be user-defined, just like ny other method, you could mke it behve in ny wy you wnted. On mutble type, it might even mutte the object! But tht would be disstrous: there s generic contrct tht clients expect equls to obey. More on this lter. Why would lnguge hve immutble types? Becuse lising is complicted, nd when you use immutble types, the issue doesn t rise. Also, code built with immutble types cn sometimes be more efficient. So if immutble types re so much simpler, why hve mutble types? Becuse muttion gives very useful form of modulrity: it llows you to mke locl chnges to structure. And muttion is often nturl wy to model entities in the rel world: trnsction on bnk ccount chnges it; it doesn t produce new bnk ccount. 3 Null References Wht does this do? String = null; System.out.println (); It prints null. The keyword null denotes vlue tht cn be tken on by n object reference. It mens tht the reference does not in fct refer to ny object. There is no null object! But note tht this code 1. String = null; 2. String b =.touppercse (); 3. System.out.println (b); behves quite differently. It throws NullPointerException on Sttement 2. We ll lern bout exceptions lter, but for now, ll you need to understnd is tht Sttement 2 filed, when the expression.touppercse() ws evluted. Wht s the difference? The receiver to method cll cn never be null, becuse it identifies the object tht receives the cll nd tht hs to be some object. So.toUpperCse() fils when is null. But in the previous exmple, System.out.println() is OK when is null, since reference is n rgument, nd this is specil kind of method (relly just plin old procedure) tht doesn t hve receiver. 4
You cn write method tht tests whether n rgument is null nd does something pproprite. Dereferencing null is common progrmming mistke in Jv. To void it, you cn check whether reference is null before you ttempt to cll method. In generl, rther thn ctching nulls nd treting them specilly, it s better to void creting null references in the first plce. You ll lern bout tht when we discuss representtion invrints. Sometimes you cn t void it, nd then it s importnt to document where the null references my occur. Tht s one reson specifictions re importnt: they cn spre you runtime errors nd unnecessry checks. Here re some questions: In generl, would you expect.equls (b) to be substitutble for b.equls ()? No, becuse when is null nd b is not, the first will throw n exception, nd the second will (usully) return flse. OK, smrty pnts, leve nulls lone. Would you then expect.equls (b) nd b.equls () to hve the sme effect? Yes, you would. In fct, this property of the equls method clled symmetry is demnded by Jv s object contrct. We ll see lter when we study equlity in depth wht other properties re required, nd how esy it is to mess up nd write n equls method tht does not hve these properties. 4 User-defined Clsses nd Fields Let s mke some objects of our own: clss Trns { int mount; Dte dte; } This code declres clss, kind of templte for mking objects. These objects re going to represent trnsctions in bnking system. Ech object hs n integer mount (which my be negtive for withdrwl), nd dte/time stmp to mrk the moment t which the trnsction occurred. The clss declres two fields or instnce vribles, mount nd dte. Ech object of the clss will contin two references, one to n integer nd one to dte. The type Dte is clss from the Jv librry; it s predefined like String (but not prt of the lnguge definition the wy String is). The type int is rther strnge best. It s not clss t ll, but primitive type. Vribles or fields of type int don t hold references to integer objects; they hold the integers themselves. You my think it bit jrring tht n object-oriented lnguge hs this rther unobject-oriented notion in it (nd mny people shre your opinion). Sometimes we ll ctully need n integer tht s n object, nd in tht cse we cn use the clss Integer from the Jv librry. How do you get from n int to n Integer nd bck? To crete n Integer, you use constructor: int_i = 5; Integer obj_i = new Integer (i); nd to extrct the primitive integer from n integer object, you cll method: 5
int i = obj_i.intvlue(); A little cumbersome, so tht s one reson people don t like this design. If we run this code: 1. Trns t = new Trns (); 2. t.mount = 20; 3. t.dte = new Dte (); fresh object gets creted, nd its fields re set, resulting in this configurtion: t dte (Dte) (Trns) mount The expression on the right-hnd side of Sttement 1 is cll to constructor: it mkes new object with defult vlues for the fields. In this cse, it cretes Trns object with zero for the mount nd null for dte. The Sttement 2 is clled setter: it sets the vlue of the field mount of the object referred to by t. Sttement 3 hs nother constructor cll on the right; creting new Dte, which by defult cretes Dte object representing the moment t which the object is itself creted. But it s lso setter: it sets the dte field of t to point to this new dte. 20 5 User-defined Constructors So we ve succeeded in mking Trns object representing deposit of twenty dollrs t this moment in time. The wy we did it creting n uninitilized object nd then setting its fields is not good one, however. We ll wnt our trnsctions to be well-formed; for exmple we won t wnt to hve trnsctions tht don t hve dtes. And perhps we ll wnt every trnsction to hve non-zero mount. Lter, we ll study these kinds of invrints in much more depth. For now, just observe tht immeditely fter Sttement 1 we hve trnsction object tht is not well formed. When n object is creted, its fields re initilized to defult vlues: null for object references, nd zero for integers. So t.mount will be zero, nd t.dte will be null. These defult vlues re rrely wht you wnt; fter ll, which vlues mke sense will depend on the problem we re trying to solve. In this cse, you d hve to know something bout bnking to know tht trnsction of zero dollrs is ill-formed. Is it big del tht there s bogus trnsction object hnging round between Sttements 1 nd 2? Yes, it is, nd here s why. We d like the responsibility for ensuring tht objects of the clss Trns re well formed to be hndled entirely within the Trns clss. In our progrm, you need to check not only the code of the clss, but lso the code tht uses the clss. At this scle, it s not disster. But in much lrger progrm, you need s much modulrity s you cn get, confining tricky spects of the progrm s much s possible to smll res of the code. To solve this problem, we declre our own constructor: clss Trns { int mount; 6
} Dte dte; Trns (int, Dte d) {mount = ; dte = d;} This constructor tkes n mount nd dte s rguments, nd cretes trnsction object with tht mount nd dte. If I d wnted to ensure tht the mount of trnsction is non-zero, I could hve dded check tht threw n exception if the mount ws zero; we ll see how to do tht lter. A peculir, but useful, property of constructors is tht hving defined our own constructor, the defult constructor the one tht just initilizes ech field to defult vlues becomes no longer vilble. So Sttement 1 will no longer compile. Insted, we cn write Trns t = new Trns (20, new Dte ()); nd tht one line of code will hve the effect tht Sttements 1 to 3 hd previously. (We hven t fully solved the problem of modulrizing the invrint of Trns, by the wy. You cn still mess up Trns object by setting its dte field to null from outside, for exmple. The first step to prevent this is to mke use of Jv s ccessibility mechnisms: we cn mke the fields privte so they cn t be red or written from outside the clss. In fct, this turns out not to be enough, s we ll lern when we study dt bstrction.) 6 Conclusion Now we ve seen ll the key notions for how objects re mnipulted in n object-oriented lnguge. We ve seen how they re creted; how references re bound to objects; nd how their fields re set. We ve mentioned the two fundmentl kinds of equlity reference equlity (tested with ==) nd object equlity (tested with n equls method bout which we ll hve much more to sy lter. These mechnisms comprise one importnt prt of wht it mens to be object oriented. 7