Closed ben-Draeger closed 5 months ago
Method calls and method references (and also lambda expressions) are very different in the byte code (invokevirtual
/invokestatic
vs. invokedynamic
)¹, and correspondingly in ArchUnit's domain model (JavaMethodCall
vs. JavaMethodReference
; see also #1221).
These different domain objects need to be addressed accordingly. There is a fluent API for both kinds of rules:
A rule using callMethodWhere
@ArchTest
ArchRule noMethodCall = noClasses().should().callMethodWhere(
target(owner(assignableTo(Target.class)).and(nameMatching("method")))
);
explicitly forbids method calls (including those from lambda expressions).
If you additionally want to forbid method references, you can use accessTargetWhere
:
@ArchTest
ArchRule noMethodAccess = noClasses().should().accessTargetWhere(
// WRONG: owner(assignableTo(Target.class)).and(nameMatching("method")) // see #1260
target(owner(assignableTo(Target.class))).and(nameMatching("method")) // Thanks to ben-Draeger for the fix! #issuecomment-1984051341
);
¹ Example:
Thanks for the clarification, accessTargetWhere() indeed works in both cases.
I will adapt our rules and that should fix the problem in our case.
However, please make sure that this is mentioned in the Documentation to enable users to choose the API that is appropriate for their usecase.
In [1], both callMethodWhere() and accessTargetWhere() do not provide any JavaDoc. Also in [2], callMethodWhere() is used 2 times without mentioning this restriction and accessTargetWhere() is not mentioned at all.
References [1] https://javadoc.io/doc/com.tngtech.archunit/archunit/latest/com/tngtech/archunit/lang/conditions/ArchConditions.html#callMethodWhere(com.tngtech.archunit.base.DescribedPredicate) [2] https://www.archunit.org/userguide/html/000_Index.html
You're clearly right that the documentation can be improved. 🙈 We also welcome pull requests to this open source project.
A note for the people that come after us:
when using accessTargetWhere(), it is better to do it this way:
@ArchTest
ArchRule noMethodAccess = noClasses().should().accessTargetWhere(
target(owner(assignableTo(Target.class))).and(nameMatching("method"))
);
Otherwise, you might run into rather subtle issues.
Aaah... 🤯 I'm sorry for that!
I have a question.
Java 8 was released in 2014[1] and introduced Closures along with Lambda expressions and method references into the language[2]. Since then, a number of issues have been opened by different people noting that ArchUnit does not support method references (see for instance #713, #215, #649). All of these issues are closed for a while now (since 2022 to be exact), so I would have expected ArchUnit to support method references by now.
However, when I test the newest version 1.2.1, I find that ArchUnit is still (in 2024 - 10 years after that feature was introduced!) unable to deal with method references.
Concretely,
given the following class whose sole method is for some reason prohibited:
and this being enforced by the following ArchUnit Rule:
then we see that this works fine on a program like
but fails to detect any problem in the following program
although the two are clearly equivalent and both programs clearly call Target.methodInQuestion() as apparent by their outputs:
Could you help me understand what is going on?
References [1] https://en.wikipedia.org/wiki/Java_version_history [2] https://www.oracle.com/java/technologies/javase/8-whats-new.html