1

We are all familiar with immutability. A very simple immutable class would be

final class Immutable {
    public final float PI = 3.14;
}

Is this truly immutable? Can Java developers really not able to modify PI? No, they can use the Reflection API to access and modify PI. However, since this is an unorthodox way of accessing a field, although the modification via Reflection API can be blocked with a Security Manager (right?), we say Immutable is immutable even if no Security Manager is activated.

interface ImmutableName {
    public String getName();
}

class MutableName implements ImmutableName {
    private String name;
    public String getName() { return name; }
    public void setName(String val) { name = val; }
}

If I have all MutableName instances referred to as ImmutableName instances, the developers modify name. Actually, the developers can check the runtime implementation of the instance then downcast the instance to MutableName. Downcasting is much more common in Java than the Reflection API, but isn't it still some unorthodox trick? Should a class designer code defensively against downcasting?

Sgene9
  • 156
  • 9
  • 2
    `class MutableName implements ImmutableName` breaks the Liskov Substitution Principle, so this seems like a problem in and of itself. For example, there could be code which relies on an `ImmutableName`'s name never changing, and passing a `MutableName` to that code would violate its expectations. – Radiodef Jul 14 '18 at 22:54
  • Please see this question as well: [Immutable class in java](https://stackoverflow.com/questions/12985059/immutable-class-in-java) – Hovercraft Full Of Eels Jul 14 '18 at 22:55
  • clearly not unorthodox. if you want your class to be immutable, it should be final, not be an interface – njzk2 Jul 14 '18 at 23:41
  • 1
    Also for information: declared like that, your PI variable is a constant. While it is still possible to mutate the field with reflection, any access to the variable through the normal syntax obj.PI will still be evaluated to the original value 3.14, because that value is resolved at compile time rather than at the time you're supposedly accessing this field (and it reality, you're not.) – kumesana Jul 15 '18 at 02:39

2 Answers2

1

You need to keep things in perspective.

Yes, it is technically possible to write code that will interfere with classes that we normally think of as immutable. (And if someone does that, the JLS says quite clearly that the behavior is not specified.)

But this is well known. And it is not normally a problem, because:

  • Responsible programmers would not that kind of thing unless it was absolutely necessary.
  • Most of our colleagues, coworkers, and so on are responsible.

So what do you do, if you really think someone might write crazy / underhand code that does this?

Solutions:

  1. Treat their code as untrusted and sandbox it.
  2. Employ comprehensive reviews on code that you need to trust.

OK, neither of these approaches are watertight. But nothing ever is in the world of IT. (Unless you are willing to disconnect your systems from the internet and work in a Faraday cage. Which is sometimes necessary ... but not an advisable approach for normal people.)


For what it is worth, the Java language and Java virtual machine design is a compromise between keeping things safe and simple (on the one hand) and providing programmers the ability to do clever and (occasionally) dangerous things in the name of performance. If the language designers went "all-in" on enforcing things like immutability:

  • the language would not be practical for much of what it is currently used for1,
  • the performance of many applications would not be competitive2, and
  • it probably wouldn't work anyway3.

1 - The kind of restrictions required would also make other things that Java developers / libraries rely on impossible. For example, the jvm features used to implement DI frameworks, serialization bindings, remoting, etcetera.

2 - No Unsafe. No native code.

3 - Clever developers would eventually find a work-around.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • What I wanted was to have some kind of factory instantiate and mutate the class then return the unmodifiable view of it and discard all references to the underlying class. It could be accomplished by wrapping the class like `Collctions.unmodifiableCollection`, but I didn't like that 1. I'm creating more objects and 2. other developers can still unwrap the object with Reflection. – Sgene9 Oct 23 '18 at 12:16
  • I understand. But I still think you are looking at this from the wrong perspective. Why do you care that developers can shoot themselves in the feet by doing manifestly stupid things with reflection (or downcasting)? And on the other side, if you don't want the (small) overhead of creating wrappers, don't do it. It is a legitimate trade-off (performance versus some degree of API safety), but you can't have it both ways. The Java type system does not **stop** someone from downcasting, whether or not it is an "orthodox" thing to do. – Stephen C Oct 23 '18 at 12:59
0

On an entirely different tack ...

Your first example asks about immutability of a specific class, and that class is considered to be immutable in the normal sense. (My other answer explains what that should be good enough.)

Your second example is about whether a class can violate the assumptions about immutability stated in an interface. And it clearly can. In fact, it is not possible to design a Java interface in a way that will force all implementations of the interface to be immutable. You can't prevent downcasting.

And similarly, you can't prevent someone doing this:

class SneakyName implements ImmutableName {
    private String name;
    public String getName() { 
       if (SomeOtherClass.beSneaky) {
           name += " ";
       }
       return name; 
    }
}

... which doesn't require downcasting to exploit.

But once again, what are we really trying to achieve here?

  • If you just want to avoid people making mistakes, then MutableName implements ImmutableName is not a mistake.

  • If you want to enforce people to do the right thing, then use code reviews. Or if you can find a static code analyzer that is good enough, to pick up willful breaking of your rules ... use that.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216