7

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"

Koen Demonie
  • 539
  • 1
  • 7
  • 24
  • 2
    Because in c++ you can send parameters as reference and pointers which enables the developer to change the original value, sometimes by mistake –  Jun 28 '15 at 18:37
  • 1
    Apropos: http://stackoverflow.com/questions/4162531/making-java-method-arguments-as-final –  Jun 28 '15 at 18:37
  • Because Java is **always** pass by value, modifying the referene inside the called method is *essentially* a no-op. While modifying the state through the reference would behave as in C++. – Elliott Frisch Jun 28 '15 at 18:41
  • 1
    You should remove the C++ tag since this is about Java; and it is a different language than C++. – Thomas Matthews Jun 28 '15 at 18:49
  • `final` in Java does not mean the same thing as `const` in C++. – Jesper Jun 28 '15 at 19:14
  • Doesn't it ? Please enlighten... – Koen Demonie Jun 28 '15 at 19:58

2 Answers2

7

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());
   }
}
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
  • Yes, but this would mean that it would be wise to set your params as final in you setters right (so you don't lose the initial given value) ? then why don't we see this as a convention ? – Koen Demonie Jun 28 '15 at 18:46
  • @KoenDemonie: I don't understand your statement above. There is practically no advantage to making a setter parameter final. What do you mean by `"so you don't lose the initial given value"`? Again, the most important take home point is to try to make your classes immutable when and wherever possible. – Hovercraft Full Of Eels Jun 28 '15 at 18:49
  • Well, if it isn't final a the setter function can change its value, and lose the initial given value...nothing too big but adding final doesn't hurt and could fix this issue – Koen Demonie Jun 28 '15 at 18:52
  • setting the param to final would enforce your statement : "a setter sets, that's it". Well that is what I think ... – Koen Demonie Jun 28 '15 at 18:56
  • You're right that reassigning an argument won't effect the passed variable, so it is a nice reminder if the compiler tells you that this won't work (as expected). And yes, this advantage is relatively small, especially for advanced developers. I personally prefer to mark every variable as `final` and to prefer immutable class. So creating setters with a `final` argument is just part of the "habbit" :D. This answer is also interessting in this matter: [Why declare final variables inside methods?](http://programmers.stackexchange.com/a/115711) – Tom Jun 28 '15 at 19:23
  • @Tom: I'm concerned with getting a false sense of security when doing this. The problem bugs are when code in one class causes unseen side effects in another, and again, declaring a parameter as final won't protect against this at all. – Hovercraft Full Of Eels Jun 28 '15 at 19:27
  • 1
    About your `BetterName` example: since you don't intend to add setter methods, you can show that even better, if each field is `final` itself. So other developers will know that this model is intended to be immutable. – Tom Jun 28 '15 at 19:27
  • @HovercraftFullOfEels This is right and this is the reason, why one should prefer both: `final` variables _and_ immutable types (and not `final` alone). And yes, there are still unproctected cases/processes one need to be aware of, but this is imo not an argument to protect yourself from unwanted reassignments. But yes (again :D) the programmer should keep in mind that `final` has its limits. – Tom Jun 28 '15 at 19:32
  • Couldn't the comment “class should be declared final” make it seem like final classes are immutable to unexperienced readers? – Bastien Léonard Jun 28 '15 at 19:39
  • thanks, sadly i cannot accept two answers. The other answer helped me a tad more to understand why we don't do it in java and we do in c++. Still great thanks ! +1 – Koen Demonie Jun 29 '15 at 16:46
5

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.)

Joop Eggen
  • 107,315
  • 7
  • 83
  • 138