Tuesday, March 22, 2011

GWT: Passing by value or by reference!

Maybe very common problem but most of the developers may forget about this developing with GWT. We know that the GWT client code will be compiled to JavaScript, HTML and so on but how the object references are handled. I made some examples and basically, what is best practice for Java is also best practice when you develop GWT. On JavaScript side you do have the same problem depending on if you pass objects or primitive types to a function.

When you pass primitive type to a function in JavaScript any changes inside the function are separated from the outside world, from everything that happens outside this function. If you pass an object, it will be passed by reference. If you change the object outside the function this will affect also the algorithm inside the function. Let’s me describe this by simple example:

1 public class DemoUser {
2
3 private String firstname;
4 private String lastname;
5 private Date age;
6
7 public DemoUser(String firstname, String lastname, Date age) {
8 this.firstname = firstname;
9 this.lastname = lastname;
10 this.age = age;
11 }
12
13 public String getFirstname() {
14 return firstname;
15 }
16
17 public String getLastname() {
18 return lastname;
19 }
20
21 public Date getAge() {
22 return age;
23 }
24
25 }


The code above looks very simple and you may thing this is an immutable class. You may use something similar inside your GWT project let’s say like a shared object. I do want to initialize this object and use it in my client code, which will be compiled to JavaScript, let’s do it like this:



1 String firstname = "John";
2 String lastname = "Familyguy";
3 Date age = new Date(); // get's the current date/time
4
5 DemoUser myuser = new DemoUser(firstname, lastname, age);


Everything good so far, this will work without any problems. So I can get the age of my user and show it in one TextBox:


1 mytextbox.setText(myuser.getAge().toString());

You will see the current date shown inside your text box. Now let’s do something else. Before you show up the date into the text box call the GWT calendar utility class and add few mode days to the age object:


1 String firstname = "John";
2 String lastname = "Familyguy";
3 Date age = new Date(); // get's the current date/time
4
5 DemoUser myuser = new DemoUser(firstname, lastname, age);
6
7 CalendarUtil.addDaysToDate(age, 2);
8
9 mytextbox.setText(myuser.getAge().toString());

You will see something completely different into your text box now. To the current date you add 2 more days and with this operation you change also the value inside your DemoUser object. You can also call the function of your object and add days there as well. This will change the value inside the DemoUser object for every next call:


1 String firstname = "John";
2 String lastname = "Familyguy";
3 Date age = new Date(); // get's the current date/time
4
5 DemoUser myuser = new DemoUser(firstname, lastname, age);
6
7 CalendarUtil.addDaysToDate(age, 2);
8
9 mytextbox.setText(myuser.getAge().toString());
10
11 CalendarUtil.addDaysToDate(myuser.getAge(), 5);
12
13 mytextbox.setText(myuser.getAge().toString());
14

Now I added one more line: CalendarUtil.addDaysToDate(myuser.getAge(), 5);  which will change again the value of our custom class. So the question is how to work around this?


The answer is already given in one of the best books for Java Developers (Effective Java) and the solution works also for the GWT applications. You do need to modify our DemoUser class and make defensive copies of the non-immutable object parameters. The class will look like this now:


1 public class DemoUser {
2
3 private String firstname;
4 private String lastname;
5 private Date age;
6
7 // required constructor with defensive copy of the age object
8 public DemoUser(String firstname, String lastname, Date age) {
9 this.firstname = firstname;
10 this.lastname = lastname;
11 this.age = new Date(age.getTime());
12 }
13
14 public String getFirstname() {
15 return firstname;
16 }
17
18 public String getLastname() {
19 return lastname;
20 }
21
22 public Date getAge() {
23 // make defensive copy here once, to prevent changes outside
24 return new Date(age.getTime());
25 }
26
27 }
28

This modified class now will prevent all modification from outside. If you try the example above again and change the age from outside the text box will still show only the date given to the DemoUser object by the initialization.


NOTE: Instead of using Date you can also use the GWT wrapper around the native JS Date object, which calls JsDate under com.google.gwt.core.client

No comments:

Post a Comment