33

I come from a C++ background and I am currently learning Java. One question arose when I have tried using some third party libraries. How do I determine if the call to a method taking an object reference as parameter modifies the object? In C++ this is clear thanks to the use of the const keyword. If the method signature is:

void foo(Boo& boo);

I know that the referenced object might be modified, while if the method signature is:

void foo(const Boo& boo);

The compiler guarantees that the referenced object is not modified.

I haven't seen something analogous in Java, as only the reference itself can be declared final, not the referenced object, and a final argument doesn't make much sense in the first place since it is passed by value anyway. Therefore, when I see a method such as:

void foo(Boo boo) {...}

How do I determine if the object referenced by boo is modified inside the body of the function (maybe using annotations)? If there is no way to know, is there some widely used convention or some best practices to avoid confusion and bugs?

Mureinik
  • 297,002
  • 52
  • 306
  • 350
ItalianMonkey
  • 331
  • 3
  • 5
  • 4
    Read the docs. (You might consider this a weakness in Java relative to C++, but we don't really miss it, any more than you miss checked exceptions when working in C++.) – user2357112 Aug 23 '15 at 07:51
  • 2
    The best practice is to avoid mutation where possible. This goes for everything, not just for arguments to methods. – usr Aug 23 '15 at 12:41
  • @user2357112 "*any more than you miss checked exceptions when working in C++"* Using exceptions to control program flow is an error, exceptions are (as its name says) for **exceptional circumstances**. Having a language feature which sole purpose is to be able to recover from an error using exceptions as the communication channel is just another case of "exceptions for flow control", a design error as the rule above states. We don't miss checked exceptions. – Manu343726 Aug 23 '15 at 17:05
  • 2
    @user2357112 the truth is that having exceptions as part of a function signature is one of the worst language design errors one could make, these are a pain when scaling/refactoring code (When an exception requirement changes in any point of a call hierarchy, that change should be propagated across all the parent functions). Hopefully C++ fixed that flaw. Java should miss `noexcept`. – Manu343726 Aug 23 '15 at 17:09
  • 1
    @Manu343726: Yes, that's why I said you don't miss them. – user2357112 Aug 23 '15 at 18:50
  • It may sound ironical, but ... one obvious way is to not call a method `foo`, but instead call it `modifyBoo` or `readBoo` ... ;-) Apart from that: I'm sometimes missing something like `const` in Java. But this "sometimes" does not outweigh the considerable efforts that it takes to make sure that a C++ program (that goes beyond a toy example) is actually fully const-correct. – Marco13 Aug 23 '15 at 20:32

7 Answers7

26

how do I determine if the object referenced by boo is modified inside the body of the function (maybe using annotations)?

The only way is to read the code unfortunately.

If there is no way to know, is there some widely used convention or some best practices to avoid confusion and bugs?

The common convention is to pass an object which cannot be modified, using a wrapper if needed. This ensure the class cannot modify the object.

List<String> readOnly = Collections.unmodifiableList(list);

If the object is Cloneable, you can also use clone() but another common approach is to use a copy.

List<String> readOnly = new ArrayList<>(list);

If you care about such behaviour, unit tests can show whether a method modifies an object or not. If you have unit tests already, it is usually one or two lines extra to check for this.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • 1
    As an advice for someone migrating from C++, it would be important to mention that you shouldn't use `clone` in Java, because the [idiom is broken](http://www.artima.com/intv/bloch13.html). – Mick Mnemonic Aug 24 '15 at 00:40
  • @MickMnemonic You should avoid calling clone() if there is a bug in it's implementation. If the object is not already Cloneable, making it Cloneable is tricky to get right esp for complex objects and should be avoided for non-trivial objects. I have seen a number of bugs which would have have been solved had `clone()` been used in the first place so avoiding it and replacing it with some more buggy isn't an answer either. – Peter Lawrey Aug 24 '15 at 07:13
  • 2
    `clone` is only useful in very few cases where a correct implementation already exists. In new code, there is no reason to expose `clone`, not even from trivial classes; just provide a copy constructor instead. – Mick Mnemonic Aug 24 '15 at 08:20
  • 1
    @MickMnemonic in short, don't write your own clone() if you can avoid it. +1 – Peter Lawrey Aug 24 '15 at 08:33
14

There's no such facility built in to the language, unfortunately. A good defensive practice is to define the data objects you pass around as immutable (i.e., without any public method that allows modifying their state). If you are really concerned about this, you could copy/clone an object before passing it to a method you don't trust, but this is usually a redundant precaution.

Mureinik
  • 297,002
  • 52
  • 306
  • 350
8

NOTE: this answer is a more detailed version of

You can also write purity or side-effect annotations in your codemernst

There exists the Checker Framework among the various things it can check at compile-time via annotations is the IJG Immutablity checker. This checker allows you to annotate object references with @Immutable or @ReadOnly.

The problem is that you often would have to annotate the library yourself. To ease your task the Checker Framework can automatically infer part of the annotations; you will still have to do much yourself.

Community
  • 1
  • 1
Kasper van den Berg
  • 8,951
  • 4
  • 48
  • 70
  • 1
    The `@Immutable` annotation enforces object immutability. The original poster was asking about reference immutability, which is provided by the `@ReadOnly` annotation. – mernst Aug 23 '15 at 16:20
  • 2
    Regarding the status of the [Checker Framework](http://checkerframework.org/), it's used by Google on hundreds of projects every day. It is also used on Wall Street and by lots of other practitioners, and by academics. It isn't an official standard and there isn't any associated JSR -- it's just a useful tool. – mernst Aug 23 '15 at 16:22
7

A side effect analysis is not built into the Java language.

You can perform side effect analysis via manual inspection, but several tools exist to automate the process.

You can use an inference tool (1, 2, 3) to detect whether your code side-effects a parameter.

You can also write purity or side-effect annotations in your code and then use a checking/verification tool (1, 2) to ensure that your code conforms to the annotations you have written.

All of the above-linked tools have limitations, but you might find them useful. If you know of other tools, mention them in comments.

mernst
  • 7,437
  • 30
  • 45
4

How do I determine if the object referenced by boo is modified inside the body of the function (maybe using annotations)?

I must agree with other answers that there is no direct way to determine that method will modify your object or not and yes to make sure that method can not modify your Object you all have to do it is from your side.

If there is no way to know, is there some widely used convention or some best practices to avoid confusion and bugs?

Here the method name comes to the scene. Moving ahead with the naming convention of method we have to take a look at some method declarations which clearly convince you that your Object will not be changed at all.

For example, You know that Arrays.copyOf will not change your actual array, System.out.println(boo) will not change your boo

Method names are real weapons to provide as much information as possible to the method user.(Yes! it's always not possible but quite a good practice to follow.)

Let's consider it in your case that say printBoo will only print, copyBoo will only copy, clearBoo will reset all attributes, checkAndCreateNewBoo will check your boo Object and create new if required.

So, ultimately if we can use them in a proper way caller can be assured with the fact that Object will remain the same after calling the method.

akash
  • 22,664
  • 11
  • 59
  • 87
  • 1
    Good point, although I've definitely seen too many times that this is violated. One important remark: you talk about "your object", which refers to the argument given to the method. That makes the answer somewhat hard to read IMHO. – Maarten Bodewes Mar 28 '23 at 15:58
2

As everyone says, prefer using immutable objects and also avoid void methods

The available purposes of methods like this

void foo(Boo boo) {...}

are to change the state of the object itself or change the object passed as a parameter

void completOrder(Order order) { ... }
//or
void parserTokenEnded(String str) { ... }
IgnazioC
  • 4,554
  • 4
  • 33
  • 46
-3

There is a way , that the method developer should mark parameter as final , if it is not going to modify the parameter.

     public void test(final Object param)

However very few people follow this , so it is difficult to know. However good programmer follow this rule , especially writing the api. If you want to write method and expose it. Make param final to indicate that passed object is not going to be modified.

Panther
  • 3,312
  • 9
  • 27
  • 50
  • 7
    Doing this is a bad idea as it doesn't do what it appears to do. It prevents the *reference* param being modified inside the method, however since this is passed by value, the caller won't be able to see if you modified it or not. It won't prevent the `param` object from being modified. – Peter Lawrey Aug 23 '15 at 07:57
  • Does this even show up in the Javadoc? Or through reflection? I'm not sure how anyone would know if you did this. – user2357112 Aug 23 '15 at 07:57
  • 1
    @Peter, this is actually a _recommended_ practice, not a bad one as it makes it clear to anyone that you shouldn't reassign the reference. You're right in that this shouldn't be confused with immutability. More discussion [in this thread](http://stackoverflow.com/questions/316352/why-would-one-mark-local-variables-and-method-parameters-as-final-in-java). – Mick Mnemonic Aug 23 '15 at 08:00
  • 2
    @MickMnemonic such a recommendation is out of date. The compiler can work out whether the field is modified or not. If your method is too long to work this out easily, the method should be broken up. You should only use `final` on fields, and rarely on other variables. – Peter Lawrey Aug 23 '15 at 08:04
  • 1
    @MickMnemonic in particular it doesn't help in the case the OP is asking about. – Peter Lawrey Aug 23 '15 at 08:05
  • @MickMnemonic, the method implementor is free to reassign the parameter if it's necessary (though it's quite rare case). There's no "contract" for local variables and method parameters, you can use them in any way you like without fear to break compatibility. If adding final to the method parameter makes something clear, it would worry me. Probably the method itself is too big and confusing and should be refactored into several simpler methods. Adding final to method parameters makes the signature longer, may introduce unnecessary linebreaks and just adds visual noise. I see no advantages. – Tagir Valeev Aug 23 '15 at 08:11
  • 1
    @PeterLawrey, I agree that this answer doesn't address OP's question but still I really can't see how declaring a method parameter as `final` would be a "bad idea" -- au contraire, it should be something that you do _by default_. It's not about compiler optimization as you seem to imply. The biggest benefit is preventing someone _accidentally_ changing the reference and causing a potentially hard-to-spot bug. Unless you declare the parameter `final`, you leave just enough room for such a bug to appear. – Mick Mnemonic Aug 24 '15 at 12:13