Graham,
I think you misunderstood a bit. There is no magic, and no setter. To better
explain the shortcoming of Mutability Detector, and the obvious solution,
please find the attached ZIP which contains a very simple maven based set
up. When reading the few source lines, you will understand the problem:
The code shows a most simple JAXB application. Class A references to
serveral instances of Class B. The reference (FIELD "bs") itself is not
mutable as it is final. There is no setter for bs. But there is a getter.
"bs" is initialized to LinkedList. Yes, LinkedList IS mutable so Mutability
Detector fails step1 of the test case, complaining about exactly that. But,
LikedList MUST be mutable, as JAXB injects the Class B instances into it.
But, EFFECTIVELY (i. e. for 99.9% of all Java programs, say, all those not
doing dirty "under-the-hood" tricks like ASM or Reflection), the LinkedList
IS IMMUTABLE as it is shielded by a defensive copy (best practice, see Josh
Bloch's "Effective Java"). In our particular scenario, the defensive copy
actually is even more effective, as it uses JRE's "unmodifiableList"
defensive wrapper builder method)! So, as we know that the LinkedList here
does no harm, we tell Mutability Detector in step2 to ignore this fact.
Step2 will succeed.
So far so good. But here come the bad news. Some silly programmer "by
incident" removes the defensive wrapper, so Class A directly returns the
LinkedList itself. NOW certainly Class A IS MUTABLE as the LinkedList is
mutable by any "normal" Java program. The horror is that Step2 still says:
"Sleep well, everything is fine.". Well, nothing is fine. This is a
false-positive!
See, this has nothing to do with strange or seldom use cases or "magic" in
any way. It has to do with the fact that checking for EFFECTIVE IMMUTABILITY
only looks at the FIELDS while it actually must look at the PROPERTIES. The
property "bs" IS EFFECTIVELY IMMUTABLE as long as it cannot be modified with
other means than ASM or Reflection. Mutability Detector's main shortcoming
is the assumption that mutability is always a FIELDS problem and could be
detected by static code analysis. But in contrast, EFFECTIVE IMMUTABILITY in
the real world of Java APIs like JAXB, JPA, and lots more, is understood as
IMMUTABLE PROPERTIES. But how to check this?
The solution is rather simple. I'm doing it like this (pseudo-code), and
Mutability Detector easily could do the same with a bit of Reflection:
* Create an instance of A (to be fully sure, this has to be done for EVERY
constructor).
* Use Reflection to inject a value into the Field A.bs.
* Call getter A.getBs().
* Try to modify the result of the getter by invoking the well-known
Collection API's modification methods (ignoring any Exceptions).
* Call getter A.getBs() again.
* Compare both collections.
* If both are equals() then the PROPERTY A.bs is EFFECTIVELY IMMUTABLE --
independent of the fact that a LinkedList was used! --> SUCCESS
* If both are NOT equals() then the PROPERTY A.bs IS MUTABLE -- independent
of the ignored reasons masking the FIELD! --> FAIL
Yes, this is complex, but working. :-)
Regards
Markus