4

I'm currently developping a multithreaded framework. To avoid side effects, I would like to require that all data manipulated by the framewok has to be immutable.

So does a way exist in Java to specify I want all subclasses of a given class or all classes implementing a given interface to be immutable?

Alexis Dufrenoy
  • 11,784
  • 12
  • 82
  • 124
  • No and I don't think there's any language with such a concept - at least I don't know one. – Voo Jul 04 '12 at 00:30
  • Perhaps not what you're after, but: you can make a class *final*, so that it cannot be subclassed. – Greg Kopff Jul 04 '12 at 00:38
  • Actually, I think it can be done with an annotation. An annotation on Types, which should be inherited and check if the type is final and also if all fields of the type are final. Annotating an interface and asking for types implementing this interface in my code should do the trick. – Alexis Dufrenoy Jul 04 '12 at 00:56
  • 1
    @Traroth: That would potentially allow you to detect it via reflection, but not enforce it to begin with (compile time). Some of the [discussion in this thread](http://groups.google.com/group/jsr-305/browse_thread/thread/fac3b88874aa8b89) is relevant. – Greg Kopff Jul 04 '12 at 01:06
  • You mean I could only detect violations at runtime? – Alexis Dufrenoy Jul 04 '12 at 01:09
  • 2
    @Traroth: *"and check if the type is final and also if all fields of the type are final"* and that these *fields* are immutable. You could have a `private final Foo foo` in your subclass - the `foo` reference can't change, but if `Foo` is mutable, so is your subclass. – Greg Kopff Jul 04 '12 at 01:38
  • @Greg Kopff: That's true. Which even for a class used for data storage (where most of the fields are primitives or String anyway) could be problematic, at least because of java.util.Date, which is mutable... – Alexis Dufrenoy Jul 04 '12 at 09:40
  • Anyway, I'm trying to write a @MustBeImmutable annotation. I will post the result here when it's done. – Alexis Dufrenoy Jul 04 '12 at 11:57
  • 1
    @Traroth the `@Immutable` from JSR-305 already fills this need. However, the problem is not in the creating of an annotation, it's in being able to enforce/check that it's actually upheld. Depending on how strict you want to be, you might have to go down routes such as installing your own Security Manager, to check that reflection isn't being used to mutate private final fields. If you're not in a situation to control that, there's always going to be ways for clients to get around your requirement. – Grundlefleck Jul 04 '12 at 21:50
  • @Traroth also, the `@Immutable` annotation I mentioned is available from Maven Central here: http://mvnrepository.com/artifact/com.google.code.findbugs/jsr305/2.0.0 – Grundlefleck Jul 04 '12 at 22:04
  • @Grundlefleck: I'm not willing to go as far. If people want to shoot themselves in the foot, so be it... – Alexis Dufrenoy Jul 04 '12 at 23:57
  • More on JSR 305: http://stackoverflow.com/questions/2289694/what-is-the-status-of-jsr-305 However, I can't see any @Immutable annotation in Java 7. Is there a known implementation? – Alexis Dufrenoy Jul 05 '12 at 00:04
  • 1
    @Traroth The @Immutable annotation did not get into Java 7, and (AFAIK) is unlikely to be added to Java 8. The annotation is available in the jar I linked to in previous comment, under the package `javax.annotation.concurrent`. However, it doesn't trigger any behaviour and there are very few automated tools that take advantage of it, so the annotation is mostly for documentation purposes. – Grundlefleck Jul 05 '12 at 08:24
  • 1
    @Voo most if not all functional programming languages even do immutability by default. (Examples: Haskell, OCaml.) Among imperative languages, immutability has started to gain attention, but most languages do it in an half-assed way (`const` in C++); I'd look in modern imperative languages to find one that has it (Kotlin, maybe). – toolforger Dec 15 '18 at 10:53
  • @toolforger There's lots of languages that offer support for immutability. That doesn't change the fact that I don't know any language that allows you to specify an interface that requires all implementing classes to be immutable. Type classes in Haskell don't allow you to do such a thing either as far as I can see. – Voo Dec 15 '18 at 13:51
  • 1
    @Voo Haskell does not have mutability at all, so there's not need to specify immutability. – toolforger Dec 16 '18 at 14:28
  • @toolforger `Data.IORef` and co might disagree with that statement. Although I was more thinking about side effects and monads there wrt Haskell. But I haven't done much Haskell in a long time, so I'm certainly the wrong person to argue the fine print on that language. – Voo Dec 16 '18 at 20:35
  • @Voo That's just faked mutability: If you keep a reference to the data before putting it into one of these faux-mutating containers, you will never see it mutate. (Admittedly, people have become quite good at faking mutability in Haskell.) – toolforger Dec 16 '18 at 22:18
  • @toolforger If you pass an `IORef` to two threads if T1 writes, T2 will see the change (ignoring things like the memory model). By any normal definition of the word that means the `IORef` is mutated. That's not just my opinion: The official documentation uses exactly that term: "Mutate the contents of an IORef." But this has nothing really to do with the question at all. – Voo Dec 16 '18 at 23:48
  • @Voo AFAIK that's just sloppy wording. I don't know the exact semantics but I'd expect reality to be something like "adds a function that defines the update that's to happen inside the IORef." But you're right in that it's some kind of mutability, and that it's wandering somewhat off-topic. – toolforger Dec 18 '18 at 07:53
  • Haskell is completly out of the scope of the question, so it would be best if you discussed that matter elsewhere, with all respect. – Alexis Dufrenoy Dec 18 '18 at 09:14

3 Answers3

3

I recommend looking into Mutability Detector. It performs static analysis to determine if a given class is immutable. It could be used to add something akin to a runtime assertion, i.e. you could choose to throw an exception if you are passed an instance of a mutable class. Note that it could analyse the actual, concrete class at runtime, including subclasses or implementations of an interface that you have defined.

It is still a pre-1.0 release, and has issues with java.lang.String, but it could be usable. If it's close to what you're looking for, but doesn't quite do what you want, I recommend contacting the mailing list or filing a bug report, as I believe the project maintainer is quite a reasonable guy. Disclaimer: that maintainer is me ;-)

Grundlefleck
  • 124,925
  • 25
  • 94
  • 111
  • I can see some uses to your tool... :-) – Alexis Dufrenoy Jul 05 '12 at 09:16
  • Thanks. I'm not going to say it's perfect, but I think it's as close as you're going to get with any open source tool. The question then becomes "is it close enough?" :) – Grundlefleck Jul 05 '12 at 09:59
  • Actually, I think I will try to use your code to write the annotation mentionned above. I write an annotation but the immutability detection is highly imperfect.... – Alexis Dufrenoy Jul 05 '12 at 11:36
2

There is no way to require immutability of subclasses. You can make sure that subclasses of your classes do not mutate your state by not providing setters and keeping your instance variables private, but if subclasses declare their own instance variables, they have full control over them.

As far as interfaces go, the only thing you can do is to not provide setters. However, implementations can provide setters of their own.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
1

To make a class truly immmutable, all members of the class must be immutable and the class itself must be final. This ensures the object cannot be changed from within the class or outside of the class.

To make members inside the class immutable, this means more than just making them final. For example

private final List<String> strings = new LinkedList<String>();

strings is final but can still be changed because items can be added and removed from the list. In this case, you could wrap it in an unmodifiable collection. Even this is not perfect though because the objects in your list could be mutable (Strings are not obviously, but your list might have mutable objects in them where you could do list.get(index).mutator(...))

There is no silver bullet as to how to make an object immutable. If it provides any methods that mutate the object, then it cannot be immutable.

As for making a class final, to achieve guaranteed immutability this necessary. Imagine the case,

class MyImmutableClass {
    private final String name
}

class MutableClass extends MyImmutableClass {
    private String mutableValue;

    public void setMutableValue(String mutableValue...)
}

void doSomething(MyImmutableClass c) {...}

There is no guarantee that doSomething is actually dealing with an immutable instance but rather it could be dealing with a mutable subclass. As you can see, this would pose a problem for working with interfaces. There is no way to guarantee that an implementor of an interface is immutable.

The @Immutable annotation does not guarantee immutability, but rather just states that the class is telling you it is immutable, but there is no good way to enforce that.

If you are able to work in groovy the @Immutable annotation has some effect as it does a lot of these techniques above that I mentioned. http://groovy.codehaus.org/Immutable+AST+Macro

Jeff Storey
  • 56,312
  • 72
  • 233
  • 406
  • 1
    Note, you can also achieve the same effect as declaring your class `final` by having only private scope constructors. (Probably not worth complicating the answer to explain this, but is worth noting imo). – Grundlefleck Jul 04 '12 at 22:07
  • Sure, you could do that. I think final more clearly expresses your intent though. – Jeff Storey Jul 04 '12 at 22:09
  • An object which holds a references to mutable objects can be immutable, if (1) within its constructor, it creates the mutable objects and mutates them as desired; (2) it does not allow a reference to any of the encapsulated objects to be leaked, directly or indirectly, to any code which might try to mutate them; (3) the immutable object will never mutate itself or any encapsulated objects after any reference to it has been exposed to the outside world. – supercat Jul 05 '12 at 20:47
  • If I have a final class C with final attribute members, if those attributes contain non-final members, is the C class really immutable? And don't even talk about reflection. When I asked this question, I didn't get immutability was such a complicated matter in Java... – Alexis Dufrenoy Jul 09 '12 at 21:06
  • No it's not truly immutable. You could do getSomeList().setSomeAttribute(x). – Jeff Storey Jul 09 '12 at 21:13
  • @JeffStorey: If a class holds the only reference to a mutable object (having created the object itself) and never exposes that reference to anyone, and if the object in question does not leak references to itself in weird ways (e.g. via static variables), is there any way that object could be mutated other than by the class which holds the reference? If the class which holds the reference doesn't mutate it, nor any other aspect of its state, outside the constructor, is there any reason it shouldn't be considered immutable? – supercat Jul 09 '12 at 23:05
  • Theoretically if you passed a list with mutable objects to a constructor and you passed that same list to something else that mutated it, even though the "immutable" class doesn't change it, the object could change. At this point though, I'm just being academic about things. – Jeff Storey Jul 10 '12 at 00:11
  • @JeffStorey: It's perfectly fine for a deeply immutable class to hold references to mutable objects if such references are recognized as *identifying* rather than *containing* their targets. For example, consider a hypothetical an immutable class `WindowSizeRestorer` which encapsulates a reference to a form object along with integers specifying its position and size; its constructor accepts a reference to a form, and its `RestoreCapturedSize` method sets that form to the previously captured size. The present location and size of the attached form would be mutable, but... – supercat Aug 13 '12 at 20:50
  • @JeffStorey: ...would not be considered to be properties of the `WindowSizeRestorer` instance. The location and other properties of the form might change, but the *identity* of the form would not. – supercat Aug 13 '12 at 20:52