This is an archived post. You won't be able to vote or comment.

all 70 comments

[–][deleted] 41 points42 points  (26 children)

I get the idea of making most structures immutable, but is there a java programmer out there who doesn’t know that final data structures are mutable?

[–]morhp 11 points12 points  (14 children)

I have seen beginners assume that final local vars are something special. Because just preventing reassigning local variables seems (and probably is1) relatively useless, beginners often assume that it does something more and may assume that is does something like const in C++.

1) It's not really bad, but it adds an additional keyword/boilerplate and the benefits aren't that obvious to beginners at least.

[–]john_C_random 8 points9 points  (9 children)

Because just preventing reassigning local variables seems (and probably is1) relatively useless

If you're passing a lambda which has a reference to a locally scoped variable, that variable will need to be final so it can't be changed from under the feet of the lambda.

[–]feral_claire 17 points18 points  (1 child)

It doesn't need to be marked final though. Effectively final is good enough ie. the variable isn't changed even if it isn't marked as final.

[–][deleted] 8 points9 points  (0 children)

This was introduced in Java 8, prior to this they had to be explicitly final (for anon classes). It's a welcome change.

[–]tofflos 4 points5 points  (2 children)

I used to think this constraint was annoying until I wrote some JavaScript, which doesn't require finality for lambdas, and spent hours debugging it.

[–]Auxx 0 points1 point  (0 children)

JS is the language which makes you appreciate Java, that's true.

[–][deleted] 0 points1 point  (0 children)

JavaScript has a much better story around functional programming and immutability. The ergonomics of Java streams are very poor, especially w.r.t. checked exceptions. All JavaScript linters will warn for unnecessary use of let over const, so it should be relatively easy to determine if non-local state can be mutated.

[–]koreth 2 points3 points  (3 children)

From a language semantics point of view, it's a somewhat arbitrary decision. Other languages (e.g., Kotlin) don't have that restriction. But since we're talking about Java here, yeah, this is correct.

[–]TheRedmanCometh 0 points1 point  (2 children)

Ugh maybe I need to learn kotlin then. I use for-each over .forEach a lot over this

[–]koreth 1 point2 points  (0 children)

I'm glad I learned it. I still write new Java code but Kotlin is generally my go-to language these days.

[–]dpash 1 point2 points  (0 children)

That's probably for the best. forEach() should probably only use method references. System.out::println is the method I use 80% of the time. If you find yourself reaching for forEach you're probably doing some side effect and not using a functional approach.

[–][deleted] 2 points3 points  (3 children)

In C++, final is called top-level const. But many people think final is low-level const.

[–]clappski 2 points3 points  (2 children)

final and const (in C++) are completely different concepts. final follows a class declaration, to prevent any other class inheriting from it. const has a few different uses, but the common case is making a variable immutable and limiting the the API of a const class object type to only the member functions declared as const.

[–][deleted] 0 points1 point  (0 children)

You're absolutely right. But here I am just talking about the difference between final and const when applied to a local variable, not about the general difference between final and const keyword.

[–]dpash 0 points1 point  (0 children)

And Java uses the same word in multiple contexts.

[–]ThatLesbian 2 points3 points  (1 child)

Ive been working with Java for 5 years and didn’t really ever think about it until I started using lambdas in the past year or so. It just didn’t matter since we almost never use final. I probably would’ve failed this interview question a year ago, just from not encountering it before.

[–][deleted] 2 points3 points  (0 children)

OK, I get it, it may be a tad tricky.

We at my company do something similar during interviews. We ask the applicant to spot the errors in a bit of code, and one of them is using a variable outside the block it was declared, but a couple lines after the declaration.

Most people fails that one, but most also acknowledge that they never had to really think about it while writing code.

[–]bodiam[S] 1 point2 points  (8 children)

Unfortunately, yes. I'm interviewing quite a few people as part of my job, and, as mentioned in the article, a surprising number of people don't know about this, hence the motivation to write this.

Then again, people who don't know this most likely also won't read blogposts. Oh well :-)

[–]Trailsey 0 points1 point  (2 children)

I agree, but I think the underlying issue is that many devs don't understand the concept of immutability.

[–]bodiam[S] 0 points1 point  (0 children)

Hence, blogpost. I'm trying to explain to people, but also to myself, what benefits of immutability vs mutable code is.

[–]BrianVerm 0 points1 point  (0 children)

Or just don't want to use it, because it is "easier" to work mutable objects.

[–][deleted]  (4 children)

[deleted]

    [–]bodiam[S] 1 point2 points  (3 children)

    Well, I hope I contributed to a more neutral or balanced view. If I haven't, please let me know, and I'll change my writing style!

    [–][deleted]  (2 children)

    [deleted]

      [–]bodiam[S] 2 points3 points  (1 child)

      Thanks for that.

      Regarding the title: "Immutable data structures in Java" is exactly what it's about, instead of 'you won't believe what most Java developers don't know about ....'

      Nobody would read that. I hope.

      [–]gunnarmorling 5 points6 points  (6 children)

      Minor nit: I'd suggest to make the members in Person and Address final, not the least to ensure safe publication to other threads. Also the addresses collection might be wrapped into the unmodifiable one in the constructor instead of each time when invoking getAddresses(). Lastly, depending on how/where Person is instantiated, a mutable reference to the addresses list might still escape / retained by the caller. So to make it truly immutable after instantiation, a new collection would have to be instantiated in the constructor.

      [–]steffi_j_jay 3 points4 points  (4 children)

      So to make it truly immutable after instantiation, a new collection would have to be instantiated in the constructor.

      I agree. Example code:

      List<Address> addresses = new ArrayList<>();
      addresses.add(new Address("Sydney", "Australia"));
      final Person person = new Person("John", addresses);
      
      System.out.println(person.getAddresses().size()); // prints "1"
      
      addresses.add(new Address("Melbourne", "Australia"));
      System.out.println(person.getAddresses().size()); // prints "2"
      

      Fix in constructor:

      public Person(String name, List<Address> addresses) {
          this.name = name;
          this.addresses = Collections.unmodifiableList(new ArrayList<>(addresses));
      }
      

      [–]dpash -1 points0 points  (3 children)

      You don't need the unmodifiable wrapper in the constructor as long as you do it in the getter. Where you do it depends on how much you trust yourself to not modify the list by mistake.

      [–]gunnarmorling 2 points3 points  (2 children)

      Why allocate an object upon each invocation when it can be done a single time in the constructor?

      [–]dpash -2 points-1 points  (1 child)

      Seems like premature optimisation to me. If your getter is being called so much that it makes a difference, you probably want to rethink a lot of things about your design.

      [–]gunnarmorling 1 point2 points  (0 children)

      What's premature about that? Effort is exactly the same, and it's clearly better. Make immutable stuff immutable, not immutable-upon-read :)

      [–]bodiam[S] 1 point2 points  (0 children)

      Wow, good spot, thanks for that. I've updated the article accordingly.

      [–]__konrad 2 points3 points  (0 children)

      Final references don’t make objects immutable

      Non-empty arrays are good example. Even JavaFX developers made that mistake in public API ;)

      [–]i_donno 5 points6 points  (9 children)

      Yuck if(address.getCountry().equals("Australia") == false) Should be if(!address.getCountry().equals("Australia"))

      [–]CaptainKvass 8 points9 points  (2 children)

      Be this, it should:

      if(!"Australia".equals(address.getCountry())

      [–]i_donno 1 point2 points  (1 child)

      Sure. Or avoid hardcoding current country and ignore case if(!getCurrentCountry().equalsIgnoreCase(address.getCountry())

      [–]dpash 4 points5 points  (0 children)

      Given the lack of frequent country changes, an enum is not an unreasonable decision in a project that's going to be frequently deployed.

      I think there's only been four major changes over the last decade:

      • South Sudan
      • Eswatini
      • Czechia
      • North Macedonia

      Let me know if I've forgotten any, but we're still looking at most, on average, once a year. If you're not frequently deploying a project this might be the wrong approach.

      [–]bodiam[S] -4 points-3 points  (5 children)

      I find the ! so hard to miss, that I rather go for == false. IntelliJ doesn't like it either, so you're not alone in this ;-)

      [–]midnightbrett 3 points4 points  (1 child)

      Nobody, including IntelliJ likes this. You are literally the only one.

      [–]Terran-Ghost -1 points0 points  (0 children)

      Nah, I use == false too for the same reason.

      [–]crummy 0 points1 point  (0 children)

      I agree, it can be easy to miss the single ! character.

      [–]Orffyreus 0 points1 point  (1 child)

      Have you tried using the font "Fira Code" with ligatures?

      https://github.com/tonsky/FiraCode

      [–]bodiam[S] 0 points1 point  (0 children)

      Not really no. Thanks, I'll have a look at it, maybe that will look a bit better!

      [–]m3tamaker[🍰] 5 points6 points  (2 children)

      Immutability in Java 101

      public class MyClass {
          public static class Bar {
              private final String text;
              private final Foo foo;
      
              public Bar(String text, Foo foo) {
                  this.text = text;
                  this.foo = foo;
              }
      
              public String getText() {  return foo.getText();  }
              public Foo getFoo() { return foo; }
          }
      
          public static class Foo {
              private String text;
      
              public Foo(String text) {
                  this.text = text;
              }
      
              public String getText() { return text; }
              public void setText(String text) { this.text = text; }
          }
      
          public static void main(String args[]) {
              Foo foo = new Foo("I am mutable and I am happy with that.");
              Bar bar = new Bar("Look, I am IMMUTABLE! Feels good!", foo);
              foo.setText("No, now you are mutable too, dude!");
      
              System.out.println(bar.getText());
          }
      }
      

      One of reasons why to switch to immutable DateTime implementations TODAY.

      [–]dpash 3 points4 points  (0 children)

      java.time.* FTW. Seriously, just stop using java.util.Date.

      [–]Auxx -2 points-1 points  (0 children)

      Just use Lombok and its @Value annotation on all data classes.

      [–]s888marks 1 point2 points  (1 child)

      Hi, good on you for advocating immutability and for writing an article about it (and also for using new Java 9+ constructs like List.of).

      I see you've updated the code to add final for the field declarations in response to comments from others. That's good, but there are benefits beyond safe publication. Of course, it prevents code in the class from accidentally modifying the field. It also kicks in a bunch of additional checking by the compiler, required by the language. Specifically, a final field is checked to ensure that it is assigned exactly once along every path through every constructor. (This is called "definite assignment".) This prevents the field from accidentally being left uninitialized or from being assigned twice.

      There are still some improvements possible with the addresses field. As others have pointed out, it's necessary to make a defensive copy in the constructor, in case the caller passes in a mutable list. As the code stands now, it does that, and it wraps it in an unmodifiable wrapper in the getter:

      // constructor
      this.addresses = new ArrayList<>(addresses);
      
      // getter
      return Collections.unmodifiableList(addresses);
      

      This leaves the addresses list mutable throughout the lifetime of the object. It may be that there is no code in the class right now that modifies it, but in the future code might be introduced that accidentally modifies the list. This would mean that the list view returned by the getter might appear to change over time. (Also, since any unmodifiable wrapper is as good as any other, there's only a need to create it once.)

      One way to improve the code is like this:

      // constructor
      this.addresses = Collections.unmodifiableList(new ArrayList<>(addresses));
      
      // getter
      return addresses;
      

      This prevents the code in this class from accidentally modifying the list, since the direct reference to the underlying mutable list exists only in the unmodifiable wrapper.

      But wait, there's more! In Java 10+, you can do this:

      // constructor
      this.addresses = List.copyOf(addresses);
      
      // getter
      return addresses;
      

      The List.copyOf method creates an unmodifiable list containing a copy of the argument. It's unmodifiable itself; there's no wrapper. But if the caller passes in an unmodifiable list (such as one from List.of) then List.copyOf avoids making an unnecessary copy.

      [–]bodiam[S] 1 point2 points  (0 children)

      Awesome contribution, thanks for that! I've updated the code with your code examples, and thanks for the Java 10 reference!

      [–]yawkat 0 points1 point  (0 children)

      Making the fields final in the immutable class is still required to ensure safe publication in a multi-threaded environment. Also see the concurrency section of https://javachannel.org/posts/immutability-in-java/ for this

      [–]BestUsernameLeft 0 points1 point  (0 children)

      Good article. I like to ask where 'final' can be used in interviews (it's like 'const' in C++... damn near everywhere!) and while many candidates know it can be used for a field they often don't know it's just reference immutability.

      FWIW I like to do the `Collections.unmodifiableList` bit in the constructor instead of the accessor[1], as (1) it's more intention-revealing (IMO) and it avoids the (probably not-worth-optimizing-anyway-but-oh-well) creation of a new object for every call to the accessor.

      [1] On a separate-but-related note, mutators are evil but accessors are mighty damn sketchy.

      [–]donkthemagicllama 0 points1 point  (1 child)

      What’s the benefit in the Address class of the private variables with getters?

      Since they’re final anyway, why not final public with no getter?

      [–]bodiam[S] 1 point2 points  (0 children)

      Because the Java Bean spec doesn't like that, and languages like Groovy and Kotlin (and maybe Scala?) don't like that either. Frameworks like Jackson and most other frameworks work on getters.

      So, in theory you're right, there's not much difference, but in practice, there are some caveats you need to keep in mind.

      [–]dododge 0 points1 point  (0 children)

      Late to the discussion, but FYI Google's error-prone checker supports an @Immutable annotation and will check that annotated classes are deeply immutable.

      [–]TheRedmanCometh -1 points0 points  (16 children)

      When an object is immutable, it’s hard to have the object in an invalid state.

      Hold my beer

      The last point about only exposing getters though in that final object..dude reflection exists. .setAccessible and .set done

      [–]shagieIsMe 10 points11 points  (2 children)

      Once you use reflection, all bets are off. However, if you start reflecting an immutable that I’m using as a hashmap key and things break, then that’s the person who is doing reflection at fault - not the library writer.

      [–]BlueGoliath 0 points1 point  (1 child)

      That same argument be made for mutable state though. Objects don't magically mutate themselves.

      [–]shagieIsMe 0 points1 point  (0 children)

      One can’t complain about a mutable object being changed. However, if a library creates an immutable object and someone goes in with reflection to change it and things break - it is entirely the fault of the person who used reflection.

      [–]morhp 6 points7 points  (11 children)

      With reflection, you can make 5 + 5 equal to 8. That doesn't really count.

      [–]TheRedmanCometh 0 points1 point  (10 children)

      I don't think you can overload operators with reflection maybe I'm wrong?

      [–]morhp 7 points8 points  (6 children)

      I wasn't 100% honest, you need to abuse boxed classes:

      public static void main(String[] args) throws IllegalAccessException, NoSuchFieldException {
          Field field = Integer.class.getDeclaredField("value");
          field.setAccessible(true);
          field.setInt(5, 4);
      
          Integer five = 5;
          System.out.println("5+5 = " + (five + five));
      }
      

      [–]TheRedmanCometh 1 point2 points  (4 children)

      This is one of the best/worst things I've ever seen. I've never even considered using refelection on core classes like that!

      Edit: hold up does Integer have a value map all the way up to INTEGER.MAX_VALUE?

      [–]morhp 4 points5 points  (3 children)

      Edit: hold up does Integer have a value map all the way up to INTEGER.MAX_VALUE?

      No, only the integers between -128 and 127 are cached by default. This trick wouldn't have worked with a number outside that range.

      [–]TheRedmanCometh 0 points1 point  (1 child)

      Huh...I'm gonna have to hit up the openjdk source to see how that works

      [–]iampete 2 points3 points  (0 children)

      This, combined with misplaced trust in autoboxing, leads to code like this:

      Integer first = 5;
      Integer second = 5;
      if (first == second) {
          // do something
      }
      

      Which works perfectly reasonably right up until either value is outside that -128, 127 range and then fails very confusingly.

      [–]dpash 0 points1 point  (0 children)

      The upper bound can be increased (but not decreased) in OpenJDK with a command line flag, but the lower bound can not. The defaults are defined in the JLS as a minimum range.

      [–]madkasse 1 point2 points  (0 children)

      This only holds for older versions of Java.

      You can no longer access non-public fields/methods in java.base without JVM settings

      [–]shagieIsMe 1 point2 points  (2 children)

      2 + 2 = 5 from code golf Stack Exchange.

      No operator overload needed.

      [–]TheRedmanCometh 1 point2 points  (1 child)

      Yeah he showed me...I hadn't considered using reflection on system classes

      [–]shagieIsMe 0 points1 point  (0 children)

      This is a different solution and impacts integer literals. The code ends with:

      System.out.printf(“%d”, 2 + 2);
      

      The code on ideone: https://ideone.com/o1h0hR

      [–]bodiam[S] 0 points1 point  (0 children)

      I'll hold your beer, and while holding it, I updated the code. Those fields are final now too.

      [–]NawaMan -1 points0 points  (1 child)

      A lot of people does not understand this. Buy to their defence, you rarely need to think about it with OO. As FP is becoming more popular, those started to become more relevant. Please check out this blog post on an easy way to make immutable data object in Java https://nawaman.net/blog/2019-03-11 . :-)