-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Issue #10859: FinalClass now exempts private-only constructor classes that are extended by another class in the same compilation unit #10968
Conversation
src/main/java/com/puppycrawl/tools/checkstyle/checks/design/FinalClassCheck.java
Show resolved
Hide resolved
9e96986
to
9560aef
Compare
8838f67
to
058f606
Compare
@pbludov Looks good to me, report is also fine, just remove
and examples related to it. Edit- |
src/main/java/com/puppycrawl/tools/checkstyle/checks/design/FinalClassCheck.java
Outdated
Show resolved
Hide resolved
Originally, it was a final String currentAstSuperClassName = getSuperClassName(classAst);
if (currentAstSuperClassName != null) { // Pitest violation
final List<ClassDesc> sameNamedClassList = classesWithSameName(
currentAstSuperClassName);
if (sameNamedClassList.isEmpty()) {
final ClassDesc classDesc = innerClasses.get(currentAstSuperClassName);
if (classDesc != null) {
classDesc.registerNestedSubclass();
}
}
else {
registerNearestAsSuperClass(qualifiedClassName, sameNamedClassList);
}
} and this is true: UPD: |
7b9ea0e
to
8cc50b3
Compare
src/main/java/com/puppycrawl/tools/checkstyle/checks/design/FinalClassCheck.java
Show resolved
Hide resolved
src/main/java/com/puppycrawl/tools/checkstyle/checks/design/FinalClassCheck.java
Show resolved
Hide resolved
return innerClasses.entrySet().stream() | ||
.filter(entry -> { | ||
return entry.getKey().endsWith( | ||
PACKAGE_SEPARATOR.concat(className)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
String.concat
used here to fix a pitest issue about redundant null
check.
String.concat
throws NPE, but PACKAGE_SEPARATOR + className
doesn't.
Github, generate report |
@nmancus1 , please finish review first. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Few minor items:
src/main/java/com/puppycrawl/tools/checkstyle/checks/design/FinalClassCheck.java
Outdated
Show resolved
Hide resolved
...test/resources/com/puppycrawl/tools/checkstyle/checks/design/finalclass/InputFinalClass.java
Outdated
Show resolved
Hide resolved
Github, generate site Link to check documentation: https://checkstyle-diff-reports.s3.us-east-2.amazonaws.com/c986f11_2021155035/config_design.html#FinalClass |
Github, generate report |
@pbludov , please fix conflict |
done |
Github, rebase |
Github, generate report |
UPDATE: it is valid violatoin as there is no extensions of this class. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I unresolved #10968 (comment)
item:
src/main/java/com/puppycrawl/tools/checkstyle/checks/design/FinalClassCheck.java
Show resolved
Hide resolved
5c8751a
to
9906988
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok to merge
src/main/java/com/puppycrawl/tools/checkstyle/checks/design/FinalClassCheck.java
Outdated
Show resolved
Hide resolved
* @return true if there is another class with same name. | ||
* @noinspection CallToStringConcatCanBeReplacedByOperator | ||
*/ | ||
private List<ClassDesc> classesWithSameName(String className) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe this connects too much to my other comment, but why not a Set (or TreeSet) ? Can we have multiples that we can't drop?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's no reason to return a set here. This method returns a list that is iterated once.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is not clear why not, especially since the code was changed to return a Collection which a Set is also subset of.
Collections.min(Collection<? extends T> coll, Comparator<? super T> comp)
, requires a Comparator in the 2nd argument.
Also using sorted
method with the stream in classesWithSameName
also requires a Comparator.
A sorted list will make finding the min the easiest as well since it will always be the first entry if it is not empty.
It is not going to gain us any compare time by combining classesWithSameName
and registerNearestAsSuperClass
to just find and return the nearest and keep the collection/list internal? Seems to me this way as it travels through the stream, it will already be tracking for the closest one in addition. registerNestedSubclassToOuterSuperClasses
just really cares if we find a nearest or not, which in a sorted list should be the first entry.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A sorted list will make finding the min the easiest as well since it will always be the first entry if it is not empty.
We need just closest class. For example,
class A {
class B {}
}
class C {
class A {
}
class E {
class F extends A {}
}
}
For class F
, the closest A
is C.A
, not the first class in the list sorted in any way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@pbludov Why won't this work? All the tests written continue to pass on my local.
diff --git a/src/main/java/com/puppycrawl/tools/checkstyle/checks/design/FinalClassCheck.java b/src/main/java/com/puppycrawl/tools/checkstyle/checks/design/FinalClassCheck.java
index edc0e68..26af1aa 100644
--- a/src/main/java/com/puppycrawl/tools/checkstyle/checks/design/FinalClassCheck.java
+++ b/src/main/java/com/puppycrawl/tools/checkstyle/checks/design/FinalClassCheck.java
@@ -20,13 +20,10 @@
package com.puppycrawl.tools.checkstyle.checks.design;
import java.util.ArrayDeque;
-import java.util.Collection;
-import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
-import java.util.stream.Collectors;
import com.puppycrawl.tools.checkstyle.FileStatefulCheck;
import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
@@ -271,13 +268,13 @@
ClassDesc currentClass) {
final String superClassName = getSuperClassName(currentClass.getClassAst());
if (superClassName != null) {
- final Collection<ClassDesc> homonyms = classesWithSameName(superClassName);
- if (homonyms.isEmpty()) {
+ final ClassDesc nearest = getNearestClassWithSameName(superClassName, qualifiedClassName);
+ if (nearest == null) {
Optional.ofNullable(innerClasses.get(superClassName))
.ifPresent(ClassDesc::registerNestedSubclass);
}
else {
- registerNearestAsSuperClass(qualifiedClassName, homonyms);
+ nearest.registerNestedSubclass();
}
}
}
@@ -289,35 +286,22 @@
* @return true if there is another class with same name.
* @noinspection CallToStringConcatCanBeReplacedByOperator
*/
- private Collection<ClassDesc> classesWithSameName(String className) {
+ private ClassDesc getNearestClassWithSameName(String className, String superClassName) {
final String dotAndClassName = PACKAGE_SEPARATOR.concat(className);
return innerClasses.entrySet().stream()
.filter(entry -> entry.getKey().endsWith(dotAndClassName))
.map(Map.Entry::getValue)
- .collect(Collectors.toUnmodifiableList());
- }
-
- /**
- * For all classes with the same name as the superclass, finds the nearest one
- * and registers a subclass for it. The nearest class is the class that have
- * the longest common prefix with the parameter {@code superClassName}.
- * If more than one class matches, the winner is the most inner class.
- *
- * @param superClassName current class name
- * @param classesWithSameName classes which same name as super class
- */
- private static void registerNearestAsSuperClass(String superClassName,
- Collection<ClassDesc> classesWithSameName) {
- final ClassDesc nearest = Collections.min(classesWithSameName, (first, second) -> {
- int diff = Integer.compare(
- classNameMatchingCount(superClassName, second.getQualifiedName()),
- classNameMatchingCount(superClassName, first.getQualifiedName()));
- if (diff == 0) {
- diff = Integer.compare(first.getDepth(), second.getDepth());
- }
- return diff;
- });
- nearest.registerNestedSubclass();
+ .sorted((first, second) -> {
+ int diff = Integer.compare(
+ classNameMatchingCount(superClassName, second.getQualifiedName()),
+ classNameMatchingCount(superClassName, first.getQualifiedName()));
+ if (diff == 0) {
+ diff = Integer.compare(first.getDepth(), second.getDepth());
+ }
+ return diff;
+ })
+ .findFirst()
+ .orElse(null);
}
/**
This is what I was talking about basically combining, the 2 into one. Sorting by nearest and selecting the first should virtually be the same as comparing each one to find nearest and looking for the min of the group. Sorting is just about organizing from min to max.
Sorting and comparing is all the things a comparator is used for and can be interchangeable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did not realize the stream had a min
as well. Your right it makes more sense as we don't need to sort everything, just find the min.
sorted has the complexity O(N*N)
Why is sorted
N^2 complexity when sorting a collection is N log N ? Is this just because of the extra, underlying work needed to be done with streams?
Edit:
Looking at my JDK's implementation of SortedOps
, I see a few usages of Arrays.sort
which is also N log N.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://stackoverflow.com/questions/49798129/what-is-more-efficient-sorted-stream-or-sorting-a-list
the sorted() method adds a component to a stream pipeline that captures all of the stream elements into either an array or an ArrayList.
If an ArrayList is used, you incur the extra overhead of building / growing the list.
as the list gets larger, the O(NlogN) sorting will tend to dominate the O(N) overheads of copying. That will mean that the relative difference between the two versions will get smaller.
Results show that Collections.sort is faster, but not by a big margin
static class SameName { // ok | ||
private SameName() { | ||
} | ||
class Test3 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe I am missing something from the requirements, but why are completely empty classes not checked?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. From the check description
Checks that a class which has only private constructors is declared as final.
And the specs says that
The default constructor has the same access modifier as the class.
Thus, if a private class has no ctor, it should be final
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Started new issue #11338
…ses that are extended by another class in the same CU
Resolves #10859: FinalClass now exempts private-only constructor classes that are extended by another class in the same compilation unit
Original PR: #10887
Diff Regression projects: https://gist.githubusercontent.com/Vyom-Yadav/91807e6244cff60698d9e33e32ba2d51/raw/eb228fa41edadc10eea264a914ea1411f0e52122/projects-to-test-on.properties
Diff Regression config: https://gist.githubusercontent.com/Vyom-Yadav/98dceb63a79f4833e85fff9b2e1464a6/raw/2e9e29be66f99b22505651aa211886535402b6cf/my_checks.xml