diff --git a/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaClass.java b/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaClass.java index 7abc0e6ca6..5c7022d1e3 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaClass.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaClass.java @@ -241,7 +241,35 @@ Optional tryGetComponentType() { } /** - * A top level class is a class that is not a nested class. + * A top level class is a class that is not a nested class, i.e. not declared within the body + * of another class.

+ * + * Example: + *

+     * public class TopLevel {
+     *     class NestedNonStatic {}
+     *     static class NestedStatic {}
+     *
+     *     void method() {
+     *         class NestedLocal {}
+     *
+     *         new NestedAnonymous() {}
+     *     }
+     * }
+     * 
+ * Of all these class declarations only {@code TopLevel} is a top level class, since all + * other classes are declared within the body of {@code TopLevel} and are thereby nested classes. + *

+ * Compare e.g. + * Java Language Specification + * + * @see #isNestedClass() + * @see #isMemberClass() + * @see #isInnerClass() + * @see #isLocalClass() + * @see #isAnonymousClass() + * @return {@code true} if this class is a top level class, i.e. not nested inside of + * any other class, {@code false} otherwise */ @PublicAPI(usage = ACCESS) public boolean isTopLevelClass() { @@ -249,11 +277,37 @@ public boolean isTopLevelClass() { } /** - * A nested class is any class whose declaration occurs - * within the body of another class or interface. + * A nested class is any class whose declaration occurs + * within the body of another class or interface.

+ * + * Example: + *

+     * public class TopLevel {
+     *     class NestedNonStatic {}
+     *     static class NestedStatic {}
      *
-     * @return Returns true if this class is declared within another class.
-     *         Returns false for top-level classes.
+     *     void method() {
+     *         class NestedLocal {}
+     *
+     *         new NestedAnonymous() {}
+     *     }
+     * }
+     * 
+ * All classes {@code NestedNonStatic}, {@code NestedStatic}, {@code NestedLocal} and the class + * the compiler creates for the anonymous class derived from {@code "new NestedAnonymous() {}"} + * (which will have some generated name like {@code TopLevel$1}) + * are considered nested classes. {@code TopLevel} on the other side is no nested class. + *

+ * Compare e.g. + * Java Language Specification + * + * @see #isTopLevelClass() + * @see #isMemberClass() + * @see #isInnerClass() + * @see #isLocalClass() + * @see #isAnonymousClass() + * @return {@code true} if this class is nested, i.e. declared within another class, + * {@code false} otherwise (i.e. for top-level classes) */ @PublicAPI(usage = ACCESS) public boolean isNestedClass() { @@ -261,8 +315,39 @@ public boolean isNestedClass() { } /** - * A member class is a class whose declaration is directly enclosed - * in the body of another class or interface declaration. + * A member class is a class whose declaration is directly enclosed + * in the body of another class or interface declaration.

+ * + * Example: + *

+     * public class TopLevel {
+     *     class MemberClassNonStatic {}
+     *     static class MemberClassStatic {}
+     *
+     *     void method() {
+     *         class NoMemberLocal {}
+     *
+     *         new NoMemberAnonymous() {}
+     *     }
+     * }
+     * 
+ * Both {@code MemberClassNonStatic} and {@code MemberClassStatic} are member classes, + * since they are directly declared within the body of {@code TopLevel}. + * On the other hand {@code NoMemberLocal} and the class + * the compiler creates for the anonymous class derived from {@code "new NoMemberAnonymous() {}"} + * (which will have some generated name like {@code TopLevel$1}), as well as {@code TopLevel} + * itself, are not considered member classes. + *

+ * Compare e.g. + * Java Language Specification + * + * @see #isTopLevelClass() + * @see #isNestedClass() + * @see #isInnerClass() + * @see #isLocalClass() + * @see #isAnonymousClass() + * @return {@code true} if this class is a member class, i.e. directly declared within + * the body of another class, {@code false} otherwise */ @PublicAPI(usage = ACCESS) public boolean isMemberClass() { @@ -270,27 +355,125 @@ public boolean isMemberClass() { } /** - * An inner class is a nested class that is not explicitly or implicitly declared static. + * An inner class is a nested class that is not explicitly or implicitly declared static.

+ * + * Example: + *

+     * public class TopLevel {
+     *     class InnerMemberClass {}
+     *     static class NoInnerClassSinceDeclaredStatic {}
+     *     interface NoInnerClassSinceInterface {}
+     *
+     *     void method() {
+     *         class InnerLocalClass {}
      *
-     * @return Returns true if this class is a non-static nested class.
-     *         Returns false otherwise.
+     *         new InnerAnonymousClass() {}
+     *     }
+     * }
+     * 
+ * The classes {@code InnerMemberClass}, {@code InnerLocalClass} and the class + * the compiler creates for the anonymous class derived from {@code "new InnerAnonymousClass() {}"} + * (which will have some generated name like {@code TopLevel$1}) + * are inner classes since they are nested but not static. + * On the other hand {@code NoInnerClassSinceDeclaredStatic}, {@code NoInnerClassSinceInterface} and {@code TopLevel} + * are no inner classes, because the former two explicitly or implicitly have the + * {@code static} modifier while the latter one is a top level class. + *

+ * Compare e.g. + * Java Language Specification + * + * @see #isTopLevelClass() + * @see #isNestedClass() + * @see #isMemberClass() + * @see #isLocalClass() + * @see #isAnonymousClass() + * @return {@code true} if this class is an inner class (i.e. nested but non-static) + * {@code false} otherwise */ @PublicAPI(usage = ACCESS) public boolean isInnerClass() { return isNestedClass() && !getModifiers().contains(JavaModifier.STATIC); } + /** + * A local class is a nested class that is not a member of any class and that has a name.

+ * + * Example: + *

+     * public class TopLevel {
+     *     class InnerClass {}
+     *     static class NestedStaticClass {}
+     *
+     *     void method() {
+     *         class LocalClass {}
+     *
+     *         new AnonymousClass() {}
+     *     }
+     * }
+     * 
+ * Only The class {@code LocalClass} is a local class, since it is a nested class that is not a member + * class, but it has the name "LocalClass".
+ * All the other classes {@code TopLevel}, {@code InnerClass}, {@code NestedStaticClass} and + * the class the compiler creates for the anonymous class derived from {@code "new AnonymousClass() {}"} + * (which will have some generated name like {@code TopLevel$1}) are considered non-local, since they + * either are top level, directly declared within the body of {@code TopLevel} or are anonymous + * and thus have no name. + *

+ * Compare e.g. + * Java Language Specification + * + * @see #isTopLevelClass() + * @see #isNestedClass() + * @see #isMemberClass() + * @see #isInnerClass() + * @see #isAnonymousClass() + * @return {@code true} if this class is local class, + * {@code false} otherwise + */ @PublicAPI(usage = ACCESS) - public boolean isAnonymousClass() { - return isAnonymousClass; + public boolean isLocalClass() { + return isNestedClass() && !isMemberClass() && !getSimpleName().isEmpty(); } /** - * A local class is a nested class that is not a member of any class and that has a name. + * An anonymous class is an inner class that is automatically derived from a class + * creation expression with a declared class body, e.g. {@code new Example(){ }}.
+ * The compiler will automatically create a class backing this instance, typically with an + * autogenerated name like {@code SomeClass$1}, where {@code SomeClass} is the class holding + * the class creation expression.

+ * + * Example: + *

+     * public class TopLevel {
+     *     class InnerClass {}
+     *     static class NestedStaticClass {}
+     *
+     *     void method() {
+     *         class LocalClass {}
+     *
+     *         new AnonymousClass() {}
+     *     }
+     * }
+     * 
+ * Only the class the compiler creates for the anonymous class derived from {@code "new AnonymousClass() {}"} + * (which will have some generated name like {@code TopLevel$1}) is considered an anonymous class.
+ * All the other classes {@code TopLevel}, {@code InnerClass}, {@code NestedStaticClass} and + * {@code LocalClass} are considered non-anonymous. + *

+ * Compare e.g. + * Java Language Specification + * + * @see #isTopLevelClass() + * @see #isNestedClass() + * @see #isMemberClass() + * @see #isInnerClass() + * @see #isLocalClass() + * @return {@code true} if this class is an anonymous class, + * {@code false} otherwise */ @PublicAPI(usage = ACCESS) - public boolean isLocalClass() { - return isNestedClass() && !isMemberClass() && !getSimpleName().isEmpty(); + public boolean isAnonymousClass() { + return isAnonymousClass; } @Override @@ -1189,18 +1372,18 @@ public boolean apply(JavaClass input) { }; @PublicAPI(usage = ACCESS) - public static final DescribedPredicate ANONYMOUS_CLASSES = new DescribedPredicate("anonymous classes") { + public static final DescribedPredicate LOCAL_CLASSES = new DescribedPredicate("local classes") { @Override public boolean apply(JavaClass input) { - return input.isAnonymousClass(); + return input.isLocalClass(); } }; @PublicAPI(usage = ACCESS) - public static final DescribedPredicate LOCAL_CLASSES = new DescribedPredicate("local classes") { + public static final DescribedPredicate ANONYMOUS_CLASSES = new DescribedPredicate("anonymous classes") { @Override public boolean apply(JavaClass input) { - return input.isLocalClass(); + return input.isAnonymousClass(); } };