Discussing the design, decisions and discovery involved in developing Mutability Detector, an open-source analysis tool for Java.

Sunday 27 January 2013

v0.9 released, java.lang.String now considered immutable!

In the past hour I just released a new version of Mutability Detector, v0.9. This release is quite significant. If you currently use Mutability Detector, I'd highly recommend upgrading. If you've never used it before, there's never been a better time to try*.

* I reserve the right to make this statement on all future releases ;-)


If I've already convinced you, here's the snippet you'll need for your pom.xml:


    <dependency>
      <groupid>org.mutabilitydetector</groupid>
      <artifactid>MutabilityDetector</artifactid>
      <version>0.9</version>
      <scope>test</scope>
    </dependency>


If I haven't convinced you yet, here's some of the new features that might get you there:

  • you can now use java.lang.String in your immutable classes, without your test failing
  • analysis recognises safe usage of Collections.unmodifiableXXX methods
  • new out-of-the-box "allowed reasons" to help you suppress false positives, in a readable and safe way
  • using the Effective Java-style builder pattern to create immutable objects should now not fail tests
  • we migrated from Google Code to GitHub, so we're in with the cool kids now, right?
  • the usual host of bug fixes and improvements

The String Problem

Finding java.lang.String to be mutable has long been a problem for Mutability Detector, recently rearing it's ugly head during a case study of ThreeTen, the new Date&Time API for Java 8. Although the general case of benign data races has still not been solved, I have taken the pragmatic choice to hard code it as immutable. Similarly for other JDK types, such as the primitive wrappers (Integer, Double, Character, etc), BigDecimal, BigInteger, and Class.

The way String et al have been hardcoded has been exposed to users, so that if you have false positives "tainting" your codebase, you too can tell Mutability Detector to regard certain types as immutable. For further information on this configuration, refer to the JavaDoc.

Unmodifiable Collections

Classes that follow this pattern:

public final class HasUnmodifiableCollection {
    private final List<String>myStrings;
 
    public HasUnmodifiableCollection(List<String> original) {
        this.myStrings = Collections.unmodifiableList(new ArrayList<String>(original));
    }
    // ...
}

... are now considered immutable. Provided you use the methods from the standard JDK to copy (the new ArrayList call) and wrap in an unmodifiable collection (the unmodifiableList call) an error won't be raised. That condition is rather strict, for example, I personally use Guava's Lists.newArrayList method to copy, and that is still flagged as an issue. Let me know how useful it is for you.


Those two additions should hopefully make a major difference in the usability of Mutability Detector. Feedback, as always, is welcome.

What's Next?

That kinda depends on the feedback I get, bug fixes that are getting in your way will likely be top priority. More ambitious plans include: allowing mutable fields, provided they are not mutated and don't escape; studying popular projects like Guava to do more case studies, and/or bundle more useful configuration.

Once the release hits Maven Central, I'll also push out a new version of the FindBugs plugin.

Let me know what you'd like to see being worked on!


8 comments:

  1. Hi,

    I just discovered your project and I'd like to give it a try.
    Could you update the download link here: https://github.com/MutabilityDetector/MutabilityDetector/downloads

    Because I don't want to use Maven :)

    Many thanks
    Jc

    ReplyDelete
  2. Hi Jc,

    I've added a wiki page with download links, since GitHub has deprecated the downloads page. The links are to the artifacts hosted on Maven Central, which you can just download from your browser. Hopefully that's not too close to Maven to put you off :)

    Link: https://github.com/MutabilityDetector/MutabilityDetector/wiki/Downloads

    Do let me know what you think.

    Regards,
    Graham

    ReplyDelete
  3. Hi Graham,

    thank you very much for your amazing tool. But my first tests failed because I still get the message "Expected: java.lang.String to be IMMUTABLE, but: java.lang.String is actually NOT_IMMUTABLE". Well I added a maven dependency to "org.mutabilitydetector / MutabilityDetector / 0.9.5" and run "MutabilityAssert.assertImmutable(myClass)".

    What am I doing wrong?

    Regards,
    Steffen

    ReplyDelete
    Replies
    1. Hi Steffen,

      Great to hear you're trying out the tool!

      With java.lang.String, you're not doing it wrong. The behaviour you see is by design. String is hardcoded to be immutable because the analysis is not powerful enough to detect its internal mutation is safe. This lead to lots of false positives of other classes, because the mutability is transitive, and kind of "infects" other classes. Hardcoding it allows classes that have String fields to still be considered immutable, but if you test the class directly, it gives the "real" result. I expected this behaviour would suit most users, as they don't make changes directly to String, instead, they use String in their own classes.

      Does that make sense?

      ~ Graham (though posting anonymous as Google won't let me log in for some reason)

      Delete
    2. Hi Graham,

      thanks for you reply! Now I understand the behaviour, though I think it's more desirable to make it consistent by design. I find it a bit confusing that String members are considered immutable, but the String class itself not. *At least* you should point that out in the documentation.

      Here's the background; I'm sure you'll find this is a perfect valid use case: I have an Event class that contains a couple of members which must be immutable and members that are not immutable. The must-be-immutable members have an annotation @EventVariable. So I wrote a Unit-Test that determines all @EventVariable members and requires these to be immutable. That works fine, until I saw that String members are not immutable. Then my research brought me to this page where I read String is considered immutable being a contradition to my unit tests.

      Best regards,
      steffen

      Delete
    3. Hi, apologies for long response time. I'm sure I replied to this, but I suspect blogspot swallowed the response.

      This is now an open issue and will hopefully be resolved soon: https://github.com/MutabilityDetector/MutabilityDetector/issues/64

      Delete
  4. Hi again,

    same with "assertInstancesOf(myClass, Matchers.anyOf(MutabilityMatchers.areImmutable(), MutabilityMatchers.areEffectivelyImmutable()));". And I get messages like "Expected: java.time.Instant to be EFFECTIVELY_IMMUTABLE, but: java.time.Instant is actually IMMUTABLE". Doesn't being immutable imply being effectively immutable?


    Regards
    Steffen

    ReplyDelete
    Replies
    1. That's an interesting point, I hadn't considered that before.

      I certainly could let the areEffectivelyImmutable() matcher accept classes that are immutable, but I'm not sure what the use case would be. Is this behaviour, albeit slightly confusing, preventing you from doing something?

      Delete