{"id":13341899,"url":"https://github.com/YvesCheung/Whisper","last_synced_at":"2025-03-11T23:30:28.623Z","repository":{"id":108866029,"uuid":"144555010","full_name":"YvesCheung/Whisper","owner":"YvesCheung","description":"一套用于代码检阅的注解","archived":false,"fork":false,"pushed_at":"2021-08-30T10:37:26.000Z","size":2823,"stargazers_count":10,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-10-24T10:08:01.060Z","etag":null,"topics":["android","annotation","inspect","lint","whisper"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/YvesCheung.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-08-13T09:04:15.000Z","updated_at":"2023-04-04T09:19:26.000Z","dependencies_parsed_at":"2023-03-29T18:32:34.409Z","dependency_job_id":null,"html_url":"https://github.com/YvesCheung/Whisper","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/YvesCheung%2FWhisper","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/YvesCheung%2FWhisper/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/YvesCheung%2FWhisper/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/YvesCheung%2FWhisper/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/YvesCheung","download_url":"https://codeload.github.com/YvesCheung/Whisper/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243129508,"owners_count":20241026,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","annotation","inspect","lint","whisper"],"created_at":"2024-07-29T19:26:40.819Z","updated_at":"2025-03-11T23:30:28.601Z","avatar_url":"https://github.com/YvesCheung.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Whisper\n\n[![Download](https://api.bintray.com/packages/yvescheung/maven/whisper/images/download.svg) ](https://bintray.com/yvescheung/maven/whisper/_latestVersion)\n[![](https://jitpack.io/v/YvesCheung/Whisper.svg)](https://jitpack.io/#YvesCheung/Whisper)\n\n\u003e 一套基于Android Lint的代码检阅规则，用于对指定注解的代码进行提示和建议\n\n![hello whisper][1]\n\n**Whisper** 是一套针对注解的代码检阅规则，作用于Android静态代码扫描，可以给开发人员在编写代码时提供代码提示和修改建议。**Whisper** 基于 [**Lint**][2] 开发，**Lint** 天然集成在 **Android Studio** 等IDE中，IDE 可以对命中规则的代码进行UI提示。\n\n**Whisper** 具有多种注解，每种注解有不同的提示作用——\n\n## @NeedInfo\n不要误以为自己能写出“自解释”的代码！充足的注释和文档可以使代码维护变得更简单。\n\n `NeedInfo` 注解提供了一种给代码 **“强提示”** 的效果。`@NeedInfo` 注解的目标可以是变量、方法或者方法参数，IDE会对方法、属性的使用方进行代码提示。 `@NeedInfo` 接受一个字符串参数，内容就是代码提示的内容。\n\n例如，在方法 `method1` 上添加注解 `@NeedInfo(\"千万不要修改这个方法，有坑！\")`，当开发者的光标悬停在 `method1` 的方法调用上，IDE就会给出 *“千万不要修改这个方法，有坑！”* 的提示。\n\n![NeedInfo][3]\n\n`@NeedWarning` `@NeedError` 是与 `@NeedInfo` 相似的注解，唯一区别是代码提示的严重等级不同。不同的严重等级会在IDE上有不同的表现，具体样式可以在IDE中自定义设置。\n\n## @UseWith\n创建和销毁通常是成对出现的方法，如果开发者只调用了创建方法而忘记销毁，就会造成内存泄漏。\n注解 `@UseWith` 的目的就是确保方法被成对调用。可以注解在 `create`、`init`、`addListener` 等自定义方法上，注解接受一个字符串参数，代表对应的析构方法的名称。\n\n![UseWith][4]\n\n## @Hide\nJava语言没有友元函数的概念。“包级私有”的可见性，调用方可以通过改包名来实现访问目的，Kotlin语言更是允许包名和文件路径不一致。Kotlin的“模块级(internal)”的可见性也有利有弊。\n\n`@Hide` 注解的目的是为了表达友元的概念，防止个别方法破坏类的封装性。\n比如常见地我们希望实现一个仅用于单元测试的方法，该方法会直接hook住关键的业务逻辑。`@org.jetbrains.annotations.TestOnly` 注解不会影响该方法的可见性，其他开发者可调用该方法而导致错误。\n\n`@Hide` 注解参数可以指定其他类的全限定符或者简单类名。只有满足参数指定的类，才可以访问注解的方法。\n\n![Hide][5]\n\n如上图，类 `ATest` 和 类 `BTest`同时访问类 `A`的方法，但只有 `ATest` 允许访问。\n\n## @DeprecatedBy\n顾名思义，`@DeprecatedBy` 和 `@java.lang.Deprecated` 作用完全相同，唯一区别是，`@DeprecatedBy` 支持一键把废弃方法替换为新方法！\n\n![DeprecatedBy][6]\n\n当Kotlin语言在 `@kotlin.Deprecated` 注解中新增了 `replaceWith` 字段的时候，我就以为会做快捷替换的功能，结果没有。因此才添加了这个注解。\n\n通常新增方法，都是在废弃的方法上增删可选的参数，或者调整参数的顺序防止与旧方法的签名一致。因此 `@DeprecatedBy` 注解具有两种方法替换的特性：\n\n- 调整原方法的参数\n\n    ```Java\n    @DeprecatedBy(\n            replaceWith = \"newMethod(%2$s, %1$s, null)\",\n            message = \"This method is deprecated, use newMethod() instead.\",\n            level = DeprecatedBy.Level.Error\n    )\n    public void oldMethod(int param1, @NonNull String param2) {\n        newMethod(param2, param1, null);\n    }\n\n    public void newMethod(@NonNull String param2, int param1, @Nullable String param3) {\n        //Do Something.\n    }\n    ```\n    \n     `replaceWith = \"newMethod(%2$s, %1$s, null)\"` 表示用 `newMethod` 来替换废弃方法，而且新方法第一个参数与旧方法的第二个参数相同，新方法第二个参数与旧方法第一个参数相同，新方法第三个参数为null。\n    \n- 更改方法的接收者为其他单例\n\n    ```Java\n    @DeprecatedBy(replaceWith = \"newMethod(%s)\", receiver = \"deprecate.demo.ClassInstead\")\n    public void oldMethod2(int param) {\n        ClassInstead.newMethod(param);\n    }\n    ```\n    \n    `receiver = \"deprecate.demo.ClassInstead\"` 表示新方法是单例 `ClassInstead` 的静态方法，旧方法 `instance.oldMethod2(0)` 会被一键替换成 `ClassInstead.newMethod(0)`。\n    \n## @Immutable\n\nJava 缺少不可变集合的概念，导致对外暴露的集合往往需要拷贝一次，或者用 `Collections.unmodifiableCollection` 方法封装一次。\n\n`@Immutable` 注解的目的是添加不可变集合的语义，使得被注解的 `List` / `Queue` / `Map` / `Map.Entry` / `Collection` / `Iterator` 无法被修改。\n\n![Immutable][7]\n\nKotlin 中由于已经有不可变集合的概念，所以不需要这个注解。\n\n## @IntDef\n\n`@IntDef` 与 `@android.support.annotation.IntDef` 作用非常相似。区别是 `@IntDef` 并不作用于注解，而是直接作用于方法或方法参数。\n\n注解的参数或方法返回值，必须为注解参数指定的枚举值，或者指定枚举值的多元表达式（比如掩码的或运算）。\n\n![IntDef][8]\n\n同理还有 `@LongDef` 和 `@StringDef` 注解。\n\n# 安装\n\n```groovy\ndependencies {\n    implementation 'com.github.YvesCheung:Whisper:$VERSION'\n}\n```\n\t\n其中 *$VERSION* 为 [![Download](https://api.bintray.com/packages/yvescheung/maven/whisper/images/download.svg)](https://bintray.com/yvescheung/maven/whisper/_latestVersion)\n   \n\n\n\n\u003e 若您还有其他想法，欢迎提交 [Issue][9] 或 [Pull Request][10]\n\n\n  [1]: https://raw.githubusercontent.com/YvesCheung/Whisper/master/art/hello-whisper.jpg\n  [2]: https://developer.android.com/studio/write/lint?hl=zh-CN\n  [3]: https://raw.githubusercontent.com/YvesCheung/Whisper/master/art/%40NeedInfo.gif\n  [4]: https://raw.githubusercontent.com/YvesCheung/Whisper/master/art/@UseWith.gif\n  [5]: https://raw.githubusercontent.com/YvesCheung/Whisper/master/art/@Hide.gif\n  [6]: https://raw.githubusercontent.com/YvesCheung/Whisper/master/art/@DeprecateBy.gif\n  [7]: https://raw.githubusercontent.com/YvesCheung/Whisper/master/art/@Immutable.gif\n  [8]: https://raw.githubusercontent.com/YvesCheung/Whisper/master/art/@IntDef.gif\n  [9]: https://github.com/YvesCheung/Whisper/issues\n  [10]: https://github.com/YvesCheung/Whisper/pulls\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FYvesCheung%2FWhisper","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FYvesCheung%2FWhisper","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FYvesCheung%2FWhisper/lists"}