1 /////////////////////////////////////////////////////////////////////////////////////////////// 2 // checkstyle: Checks Java source code and other text files for adherence to a set of rules. 3 // Copyright (C) 2001-2025 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 * <div class="wrapper"><pre class="prettyprint"><code class="language-java"> 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<Address> 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 * </code></pre></div> 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 * Setter to control whether to enforce that {@code public} is explicitly coded 170 * on interface fields. 171 * 172 * @param violateImpliedPublicField 173 * True to perform the check, false to turn the check off. 174 * @since 8.12 175 */ 176 public void setViolateImpliedPublicField(boolean violateImpliedPublicField) { 177 this.violateImpliedPublicField = violateImpliedPublicField; 178 } 179 180 /** 181 * Setter to control whether to enforce that {@code static} is explicitly coded 182 * on interface fields. 183 * 184 * @param violateImpliedStaticField 185 * True to perform the check, false to turn the check off. 186 * @since 8.12 187 */ 188 public void setViolateImpliedStaticField(boolean violateImpliedStaticField) { 189 this.violateImpliedStaticField = violateImpliedStaticField; 190 } 191 192 /** 193 * Setter to control whether to enforce that {@code final} is explicitly coded 194 * on interface fields. 195 * 196 * @param violateImpliedFinalField 197 * True to perform the check, false to turn the check off. 198 * @since 8.12 199 */ 200 public void setViolateImpliedFinalField(boolean violateImpliedFinalField) { 201 this.violateImpliedFinalField = violateImpliedFinalField; 202 } 203 204 /** 205 * Setter to control whether to enforce that {@code public} is explicitly coded 206 * on interface methods. 207 * 208 * @param violateImpliedPublicMethod 209 * True to perform the check, false to turn the check off. 210 * @since 8.12 211 */ 212 public void setViolateImpliedPublicMethod(boolean violateImpliedPublicMethod) { 213 this.violateImpliedPublicMethod = violateImpliedPublicMethod; 214 } 215 216 /** 217 * Setter to control whether to enforce that {@code abstract} is explicitly coded 218 * on interface methods. 219 * 220 * @param violateImpliedAbstractMethod 221 * True to perform the check, false to turn the check off. 222 * @since 8.12 223 */ 224 public void setViolateImpliedAbstractMethod(boolean violateImpliedAbstractMethod) { 225 this.violateImpliedAbstractMethod = violateImpliedAbstractMethod; 226 } 227 228 /** 229 * Setter to control whether to enforce that {@code public} is explicitly coded 230 * on interface nested types. 231 * 232 * @param violateImpliedPublicNested 233 * True to perform the check, false to turn the check off. 234 * @since 8.12 235 */ 236 public void setViolateImpliedPublicNested(boolean violateImpliedPublicNested) { 237 this.violateImpliedPublicNested = violateImpliedPublicNested; 238 } 239 240 /** 241 * Setter to control whether to enforce that {@code static} is explicitly coded 242 * on interface nested types. 243 * 244 * @param violateImpliedStaticNested 245 * True to perform the check, false to turn the check off. 246 * @since 8.12 247 */ 248 public void setViolateImpliedStaticNested(boolean violateImpliedStaticNested) { 249 this.violateImpliedStaticNested = violateImpliedStaticNested; 250 } 251 252 @Override 253 public int[] getDefaultTokens() { 254 return getAcceptableTokens(); 255 } 256 257 @Override 258 public int[] getRequiredTokens() { 259 return getAcceptableTokens(); 260 } 261 262 @Override 263 public int[] getAcceptableTokens() { 264 return new int[] { 265 TokenTypes.METHOD_DEF, 266 TokenTypes.VARIABLE_DEF, 267 TokenTypes.INTERFACE_DEF, 268 TokenTypes.CLASS_DEF, 269 TokenTypes.ENUM_DEF, 270 }; 271 } 272 273 @Override 274 public void visitToken(DetailAST ast) { 275 if (ScopeUtil.isInInterfaceBlock(ast) && !ScopeUtil.isInCodeBlock(ast)) { 276 switch (ast.getType()) { 277 case TokenTypes.METHOD_DEF -> processMethod(ast); 278 279 case TokenTypes.VARIABLE_DEF -> processField(ast); 280 281 case TokenTypes.CLASS_DEF, 282 TokenTypes.INTERFACE_DEF, 283 TokenTypes.ENUM_DEF -> processNestedType(ast); 284 285 default -> throw new IllegalStateException(ast.toString()); 286 } 287 } 288 } 289 290 /** 291 * Check method in interface. 292 * 293 * @param ast the method AST 294 */ 295 private void processMethod(DetailAST ast) { 296 final DetailAST modifiers = ast.findFirstToken(TokenTypes.MODIFIERS); 297 if (violateImpliedPublicMethod 298 && modifiers.findFirstToken(TokenTypes.LITERAL_PRIVATE) == null 299 && modifiers.findFirstToken(TokenTypes.LITERAL_PUBLIC) == null) { 300 log(ast, MSG_KEY, PUBLIC_ACCESS_MODIFIER); 301 } 302 if (violateImpliedAbstractMethod 303 && modifiers.findFirstToken(TokenTypes.LITERAL_PRIVATE) == null 304 && modifiers.findFirstToken(TokenTypes.LITERAL_STATIC) == null 305 && modifiers.findFirstToken(TokenTypes.LITERAL_DEFAULT) == null 306 && modifiers.findFirstToken(TokenTypes.ABSTRACT) == null) { 307 log(ast, MSG_KEY, ABSTRACT_KEYWORD); 308 } 309 } 310 311 /** 312 * Check field in interface. 313 * 314 * @param ast the field AST 315 */ 316 private void processField(DetailAST ast) { 317 final DetailAST modifiers = ast.findFirstToken(TokenTypes.MODIFIERS); 318 if (violateImpliedPublicField 319 && modifiers.findFirstToken(TokenTypes.LITERAL_PUBLIC) == null) { 320 log(ast, MSG_KEY, PUBLIC_ACCESS_MODIFIER); 321 } 322 if (violateImpliedStaticField 323 && modifiers.findFirstToken(TokenTypes.LITERAL_STATIC) == null) { 324 log(ast, MSG_KEY, STATIC_KEYWORD); 325 } 326 if (violateImpliedFinalField 327 && modifiers.findFirstToken(TokenTypes.FINAL) == null) { 328 log(ast, MSG_KEY, FINAL_KEYWORD); 329 } 330 } 331 332 /** 333 * Check nested types in interface. 334 * 335 * @param ast the nested type AST 336 */ 337 private void processNestedType(DetailAST ast) { 338 final DetailAST modifiers = ast.findFirstToken(TokenTypes.MODIFIERS); 339 if (violateImpliedPublicNested 340 && modifiers.findFirstToken(TokenTypes.LITERAL_PUBLIC) == null) { 341 log(ast, MSG_KEY, PUBLIC_ACCESS_MODIFIER); 342 } 343 if (violateImpliedStaticNested 344 && modifiers.findFirstToken(TokenTypes.LITERAL_STATIC) == null) { 345 log(ast, MSG_KEY, STATIC_KEYWORD); 346 } 347 } 348 349 }