001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2026 the original author or authors. 004// 005// This library is free software; you can redistribute it and/or 006// modify it under the terms of the GNU Lesser General Public 007// License as published by the Free Software Foundation; either 008// version 2.1 of the License, or (at your option) any later version. 009// 010// This library is distributed in the hope that it will be useful, 011// but WITHOUT ANY WARRANTY; without even the implied warranty of 012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013// Lesser General Public License for more details. 014// 015// You should have received a copy of the GNU Lesser General Public 016// License along with this library; if not, write to the Free Software 017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 018/////////////////////////////////////////////////////////////////////////////////////////////// 019 020package com.puppycrawl.tools.checkstyle.checks.modifier; 021 022import com.puppycrawl.tools.checkstyle.StatelessCheck; 023import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 024import com.puppycrawl.tools.checkstyle.api.DetailAST; 025import com.puppycrawl.tools.checkstyle.api.TokenTypes; 026import com.puppycrawl.tools.checkstyle.utils.ScopeUtil; 027 028/** 029 * <div> 030 * Checks for implicit modifiers on interface members and nested types. 031 * </div> 032 * 033 * <p> 034 * This check is effectively the opposite of 035 * <a href="https://checkstyle.org/checks/modifier/redundantmodifier.html"> 036 * RedundantModifier</a>. 037 * It checks the modifiers on interface members, ensuring that certain modifiers are explicitly 038 * specified even though they are actually redundant. 039 * </p> 040 * 041 * <p> 042 * Methods in interfaces are {@code public} by default, however from Java 9 they can also be 043 * {@code private}. This check provides the ability to enforce that {@code public} is explicitly 044 * coded and not implicitly added by the compiler. 045 * </p> 046 * 047 * <p> 048 * From Java 8, there are three types of methods in interfaces - static methods marked with 049 * {@code static}, default methods marked with {@code default} and abstract methods which do not 050 * have to be marked with anything. From Java 9, there are also private methods marked with 051 * {@code private}. This check provides the ability to enforce that {@code abstract} is 052 * explicitly coded and not implicitly added by the compiler. 053 * </p> 054 * 055 * <p> 056 * Fields in interfaces are always {@code public static final} and as such the compiler does not 057 * require these modifiers. This check provides the ability to enforce that these modifiers are 058 * explicitly coded and not implicitly added by the compiler. 059 * </p> 060 * 061 * <p> 062 * Nested types within an interface are always {@code public static} and as such the compiler 063 * does not require the {@code public static} modifiers. This check provides the ability to 064 * enforce that the {@code public} and {@code static} modifiers are explicitly coded and not 065 * implicitly added by the compiler. 066 * </p> 067 * <div class="wrapper"><pre class="prettyprint"><code class="language-java"> 068 * public interface AddressFactory { 069 * // check enforces code contains "public static final" 070 * public static final String UNKNOWN = "Unknown"; 071 * 072 * String OTHER = "Other"; // violation 073 * 074 * // check enforces code contains "public" or "private" 075 * public static AddressFactory instance(); 076 * 077 * // check enforces code contains "public abstract" 078 * public abstract Address createAddress(String addressLine, String city); 079 * 080 * List<Address> findAddresses(String city); // violation 081 * 082 * // check enforces default methods are explicitly declared "public" 083 * public default Address createAddress(String city) { 084 * return createAddress(UNKNOWN, city); 085 * } 086 * 087 * default Address createOtherAddress() { // violation 088 * return createAddress(OTHER, OTHER); 089 * } 090 * } 091 * </code></pre></div> 092 * 093 * <p> 094 * Rationale for this check: Methods, fields and nested types are treated differently 095 * depending on whether they are part of an interface or part of a class. For example, by 096 * default methods are package-scoped on classes, but public in interfaces. However, from 097 * Java 8 onwards, interfaces have changed to be much more like abstract classes. 098 * Interfaces now have static and instance methods with code. Developers should not have to 099 * 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 106public 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}