Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Setter params final in Java

I have always been programming in java, and recently i started learning some c++.

In C++ it is conventional to set setter params as const, why don't we see this as much in java ?

I mean are there any disadvantages to creating a setter like so:

public void setObject(final Object o){ this.o=o; }

vs

public void setObject(Object o){ this.o=o; }

The first one should enforce for Object param o to stay constant through the whole set function, not ?

Edit:

A final param would enforce this NOT to happen :

public void setName(String name){ 
     name="Carlos";
     this.name=name; 
}

The user will never be able to set the name different from "Carlos"

like image 631
Koen Demonie Avatar asked Jun 28 '15 18:06

Koen Demonie


2 Answers

There's little advantage to setting a Java method parameter as final since it does not stop someone from changing the parameter reference's state within the method. All it prevents is the re-assignment of the parameter variable to something else, which does nothing to the original reference, and it allows for use of the parameter in anonymous inner classes. If you wanted true safety in this situation, you'd strive to make your parameter types immutable if possible.


Edit
You've posted:

public void setObject(Object o){ 
     o++; // this does not compile
     this.o=o; 
}

Which mixes primitive numeric and reference type. It only makes sense if o is an Integer or other numeric wrapper class, and even so, making it final would not prevent someone from creating:

private void setI(final Integer i) {
   this.i = 1 + i;
}

But neither your code nor this code above would affect the parameter object on the calling code side.


Edit
OK now you've posted:

public void setName(String name){ 
     name="Carlos";
     this.name=name; 
}

But then someone could write

public void setName(final String name){ 
     this.name= name + " Carlos"; 
}

Here's where the danger comes and where final doesn't help. Say you have a class called Name:

public class Name {
   private String lastName;
   private String firstName;
   public Name(String lastName, String firstName) {
      this.lastName = lastName;
      this.firstName = firstName;
   }
   public String getLastName() {
      return lastName;
   }
   public void setLastName(String lastName) {
      this.lastName = lastName;
   }
   public String getFirstName() {
      return firstName;
   }
   public void setFirstName(String firstName) {
      this.firstName = firstName;
   }
}

And then a class, Foo, with a Name field and a setter. This is dangerous code:

class Foo {
   private Name name;

   public void setName(final Name name) {
      name.setFirstName("Carlos");
      this.name = name;
   }
}

Because not only does it change the state of the field, it changes the state of the Name reference in the calling code, and the final modifier won't help one bit. The solution: make Name immutable.

e.g.,

import java.util.Date;

// class should be declared final
public final class BetterName {
   private String lastName;
   private String firstName;
   private Date dateOfBirth;

   public BetterName(String lastName, String firstName, Date dob) {
      this.lastName = lastName;
      this.firstName = firstName;

      // make and store a private copy of non-immutable reference types
      dateOfBirth = new Date(dob.getTime()); 
   }

   // only getters -- no setters
   public String getLastName() {
      return lastName;
   }

   public String getFirstName() {
      return firstName;
   }

   public Date getDateOfBirth() {
      // return copies of non-immutable fields
      return new Date(dateOfBirth.getTime());
   }
}
like image 186
Hovercraft Full Of Eels Avatar answered Oct 24 '22 17:10

Hovercraft Full Of Eels


Okay, a final parameter/variable cannot be assigned to. As the java compiler needs to be capable to determine if a variable/parameter is actually final (for anonymous inner classes), optimization is no factor AFAIK.

It is more that C++ has a larger tool set, which java tried to reduce. Hence using C++ const string& is important, saying

  1. The string is passed by pointer, access is automatically dereferenced.
  2. If the actual argument is a variable, the variable itself is not changed.
  3. Mind there might be a conversion operator for passing something else than a const string&.

Now java:

  1. Java does not allocate objects on the stack, only keeps primitive types and object handles on the stack.
  2. Java has not output parameters: a variable passed to a method call will never change its immediate value.

Back to your question:

As a setter in java mostly would not benefit from a final parameter. A final will be a contract to not use the variable for a second assignment.

However:

public final void setXyz(Xz xyz) {
    this.xyz = xyz;
}

is more useful: this method cannot be overriden, and hence may be safely used in a constructor. (Calling an overriden method in a constructor would be in a context of a still not initialized child instance.)

like image 32
Joop Eggen Avatar answered Oct 24 '22 16:10

Joop Eggen