View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2026 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ///////////////////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle.checks.modifier;
21  
22  import com.puppycrawl.tools.checkstyle.StatelessCheck;
23  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
24  import com.puppycrawl.tools.checkstyle.api.DetailAST;
25  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
26  import com.puppycrawl.tools.checkstyle.utils.ScopeUtil;
27  
28  /**
29   * <div>
30   * Checks for implicit modifiers on interface members and nested types.
31   * </div>
32   *
33   * <p>
34   * This check is effectively the opposite of
35   * <a href="https://checkstyle.org/checks/modifier/redundantmodifier.html">
36   * RedundantModifier</a>.
37   * It checks the modifiers on interface members, ensuring that certain modifiers are explicitly
38   * specified even though they are actually redundant.
39   * </p>
40   *
41   * <p>
42   * Methods in interfaces are {@code public} by default, however from Java 9 they can also be
43   * {@code private}. This check provides the ability to enforce that {@code public} is explicitly
44   * coded and not implicitly added by the compiler.
45   * </p>
46   *
47   * <p>
48   * From Java 8, there are three types of methods in interfaces - static methods marked with
49   * {@code static}, default methods marked with {@code default} and abstract methods which do not
50   * have to be marked with anything. From Java 9, there are also private methods marked with
51   * {@code private}. This check provides the ability to enforce that {@code abstract} is
52   * explicitly coded and not implicitly added by the compiler.
53   * </p>
54   *
55   * <p>
56   * Fields in interfaces are always {@code public static final} and as such the compiler does not
57   * require these modifiers. This check provides the ability to enforce that these modifiers are
58   * explicitly coded and not implicitly added by the compiler.
59   * </p>
60   *
61   * <p>
62   * Nested types within an interface are always {@code public static} and as such the compiler
63   * does not require the {@code public static} modifiers. This check provides the ability to
64   * enforce that the {@code public} and {@code static} modifiers are explicitly coded and not
65   * implicitly added by the compiler.
66   * </p>
67   * {@snippet lang="text" :
68   * public interface AddressFactory {
69   *   // check enforces code contains "public static final"
70   *   public static final String UNKNOWN = "Unknown";
71   *
72   *   String OTHER = "Other";  // violation
73   *
74   *   // check enforces code contains "public" or "private"
75   *   public static AddressFactory instance();
76   *
77   *   // check enforces code contains "public abstract"
78   *   public abstract Address createAddress(String addressLine, String city);
79   *
80   *   List&lt;Address&gt; findAddresses(String city);  // violation
81   *
82   *   // check enforces default methods are explicitly declared "public"
83   *   public default Address createAddress(String city) {
84   *     return createAddress(UNKNOWN, city);
85   *   }
86   *
87   *   default Address createOtherAddress() {  // violation
88   *     return createAddress(OTHER, OTHER);
89   *   }
90   * }
91   * }
92   *
93   * <p>
94   * Rationale for this check: Methods, fields and nested types are treated differently
95   * depending on whether they are part of an interface or part of a class. For example, by
96   * default methods are package-scoped on classes, but public in interfaces. However, from
97   * Java 8 onwards, interfaces have changed to be much more like abstract classes.
98   * Interfaces now have static and instance methods with code. Developers should not have to
99   * remember which modifiers are required and which are implied. This check allows the simpler
100  * alternative approach to be adopted where the implied modifiers must always be coded explicitly.
101  * </p>
102  *
103  * @since 8.12
104  */
105 @StatelessCheck
106 public class InterfaceMemberImpliedModifierCheck
107     extends AbstractCheck {
108 
109     /**
110      * A key is pointing to the warning message text in "messages.properties" file.
111      */
112     public static final String MSG_KEY = "interface.implied.modifier";
113 
114     /** Name for 'public' access modifier. */
115     private static final String PUBLIC_ACCESS_MODIFIER = "public";
116 
117     /** Name for 'abstract' keyword. */
118     private static final String ABSTRACT_KEYWORD = "abstract";
119 
120     /** Name for 'static' keyword. */
121     private static final String STATIC_KEYWORD = "static";
122 
123     /** Name for 'final' keyword. */
124     private static final String FINAL_KEYWORD = "final";
125 
126     /**
127      * Control whether to enforce that {@code public} is explicitly coded
128      * on interface fields.
129      */
130     private boolean violateImpliedPublicField = true;
131 
132     /**
133      * Control whether to enforce that {@code static} is explicitly coded
134      * on interface fields.
135      */
136     private boolean violateImpliedStaticField = true;
137 
138     /**
139      * Control whether to enforce that {@code final} is explicitly coded
140      * on interface fields.
141      */
142     private boolean violateImpliedFinalField = true;
143 
144     /**
145      * Control whether to enforce that {@code public} is explicitly coded
146      * on interface methods.
147      */
148     private boolean violateImpliedPublicMethod = true;
149 
150     /**
151      * Control whether to enforce that {@code abstract} is explicitly coded
152      * on interface methods.
153      */
154     private boolean violateImpliedAbstractMethod = true;
155 
156     /**
157      * Control whether to enforce that {@code public} is explicitly coded
158      * on interface nested types.
159      */
160     private boolean violateImpliedPublicNested = true;
161 
162     /**
163      * Control whether to enforce that {@code static} is explicitly coded
164      * on interface nested types.
165      */
166     private boolean violateImpliedStaticNested = true;
167 
168     /**
169      * Creates a new {@code InterfaceMemberImpliedModifierCheck} instance.
170      */
171     public InterfaceMemberImpliedModifierCheck() {
172         // no code by default
173     }
174 
175     /**
176      * Setter to control whether to enforce that {@code public} is explicitly coded
177      * on interface fields.
178      *
179      * @param violateImpliedPublicField
180      *        True to perform the check, false to turn the check off.
181      * @since 8.12
182      */
183     public void setViolateImpliedPublicField(boolean violateImpliedPublicField) {
184         this.violateImpliedPublicField = violateImpliedPublicField;
185     }
186 
187     /**
188      * Setter to control whether to enforce that {@code static} is explicitly coded
189      * on interface fields.
190      *
191      * @param violateImpliedStaticField
192      *        True to perform the check, false to turn the check off.
193      * @since 8.12
194      */
195     public void setViolateImpliedStaticField(boolean violateImpliedStaticField) {
196         this.violateImpliedStaticField = violateImpliedStaticField;
197     }
198 
199     /**
200      * Setter to control whether to enforce that {@code final} is explicitly coded
201      * on interface fields.
202      *
203      * @param violateImpliedFinalField
204      *        True to perform the check, false to turn the check off.
205      * @since 8.12
206      */
207     public void setViolateImpliedFinalField(boolean violateImpliedFinalField) {
208         this.violateImpliedFinalField = violateImpliedFinalField;
209     }
210 
211     /**
212      * Setter to control whether to enforce that {@code public} is explicitly coded
213      * on interface methods.
214      *
215      * @param violateImpliedPublicMethod
216      *        True to perform the check, false to turn the check off.
217      * @since 8.12
218      */
219     public void setViolateImpliedPublicMethod(boolean violateImpliedPublicMethod) {
220         this.violateImpliedPublicMethod = violateImpliedPublicMethod;
221     }
222 
223     /**
224      * Setter to control whether to enforce that {@code abstract} is explicitly coded
225      * on interface methods.
226      *
227      * @param violateImpliedAbstractMethod
228      *        True to perform the check, false to turn the check off.
229      * @since 8.12
230      */
231     public void setViolateImpliedAbstractMethod(boolean violateImpliedAbstractMethod) {
232         this.violateImpliedAbstractMethod = violateImpliedAbstractMethod;
233     }
234 
235     /**
236      * Setter to control whether to enforce that {@code public} is explicitly coded
237      * on interface nested types.
238      *
239      * @param violateImpliedPublicNested
240      *        True to perform the check, false to turn the check off.
241      * @since 8.12
242      */
243     public void setViolateImpliedPublicNested(boolean violateImpliedPublicNested) {
244         this.violateImpliedPublicNested = violateImpliedPublicNested;
245     }
246 
247     /**
248      * Setter to control whether to enforce that {@code static} is explicitly coded
249      * on interface nested types.
250      *
251      * @param violateImpliedStaticNested
252      *        True to perform the check, false to turn the check off.
253      * @since 8.12
254      */
255     public void setViolateImpliedStaticNested(boolean violateImpliedStaticNested) {
256         this.violateImpliedStaticNested = violateImpliedStaticNested;
257     }
258 
259     @Override
260     public int[] getDefaultTokens() {
261         return getAcceptableTokens();
262     }
263 
264     @Override
265     public int[] getRequiredTokens() {
266         return getAcceptableTokens();
267     }
268 
269     @Override
270     public int[] getAcceptableTokens() {
271         return new int[] {
272             TokenTypes.METHOD_DEF,
273             TokenTypes.VARIABLE_DEF,
274             TokenTypes.INTERFACE_DEF,
275             TokenTypes.CLASS_DEF,
276             TokenTypes.ENUM_DEF,
277         };
278     }
279 
280     @Override
281     public void visitToken(DetailAST ast) {
282         if (ScopeUtil.isInInterfaceBlock(ast) && !ScopeUtil.isInCodeBlock(ast)) {
283             switch (ast.getType()) {
284                 case TokenTypes.METHOD_DEF -> processMethod(ast);
285 
286                 case TokenTypes.VARIABLE_DEF -> processField(ast);
287 
288                 case TokenTypes.CLASS_DEF,
289                      TokenTypes.INTERFACE_DEF,
290                      TokenTypes.ENUM_DEF -> processNestedType(ast);
291 
292                 default -> throw new IllegalStateException(ast.toString());
293             }
294         }
295     }
296 
297     /**
298      * Check method in interface.
299      *
300      * @param ast the method AST
301      */
302     private void processMethod(DetailAST ast) {
303         final DetailAST modifiers = ast.findFirstToken(TokenTypes.MODIFIERS);
304         if (violateImpliedPublicMethod
305                 && modifiers.findFirstToken(TokenTypes.LITERAL_PRIVATE) == null
306                 && modifiers.findFirstToken(TokenTypes.LITERAL_PUBLIC) == null) {
307             log(ast, MSG_KEY, PUBLIC_ACCESS_MODIFIER);
308         }
309         if (violateImpliedAbstractMethod
310                 && modifiers.findFirstToken(TokenTypes.LITERAL_PRIVATE) == null
311                 && modifiers.findFirstToken(TokenTypes.LITERAL_STATIC) == null
312                 && modifiers.findFirstToken(TokenTypes.LITERAL_DEFAULT) == null
313                 && modifiers.findFirstToken(TokenTypes.ABSTRACT) == null) {
314             log(ast, MSG_KEY, ABSTRACT_KEYWORD);
315         }
316     }
317 
318     /**
319      * Check field in interface.
320      *
321      * @param ast the field AST
322      */
323     private void processField(DetailAST ast) {
324         final DetailAST modifiers = ast.findFirstToken(TokenTypes.MODIFIERS);
325         if (violateImpliedPublicField
326                 && modifiers.findFirstToken(TokenTypes.LITERAL_PUBLIC) == null) {
327             log(ast, MSG_KEY, PUBLIC_ACCESS_MODIFIER);
328         }
329         if (violateImpliedStaticField
330                 && modifiers.findFirstToken(TokenTypes.LITERAL_STATIC) == null) {
331             log(ast, MSG_KEY, STATIC_KEYWORD);
332         }
333         if (violateImpliedFinalField
334                 && modifiers.findFirstToken(TokenTypes.FINAL) == null) {
335             log(ast, MSG_KEY, FINAL_KEYWORD);
336         }
337     }
338 
339     /**
340      * Check nested types in interface.
341      *
342      * @param ast the nested type AST
343      */
344     private void processNestedType(DetailAST ast) {
345         final DetailAST modifiers = ast.findFirstToken(TokenTypes.MODIFIERS);
346         if (violateImpliedPublicNested
347                 && modifiers.findFirstToken(TokenTypes.LITERAL_PUBLIC) == null) {
348             log(ast, MSG_KEY, PUBLIC_ACCESS_MODIFIER);
349         }
350         if (violateImpliedStaticNested
351                 && modifiers.findFirstToken(TokenTypes.LITERAL_STATIC) == null) {
352             log(ast, MSG_KEY, STATIC_KEYWORD);
353         }
354     }
355 
356 }