只要我记得,我总是错过Java中的类型别名(例如c ++中的typedef或haskell中的type / newtype)。
在Android SDK中,我们有support annotations,其中包含@IntDef
,@StringDef
和各种资源类型注释,这有助于我们在编译时找出可能滥用整数/字符串值的问题。我正在从Android文档中插入一段代码,为您提供简短的想法:
@IntDef({NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS})
@Retention(RetentionPolicy.SOURCE)
public @interface NavigationMode {}
public static final int NAVIGATION_MODE_STANDARD = 0;
public static final int NAVIGATION_MODE_LIST = 1;
public static final int NAVIGATION_MODE_TABS = 2;
@NavigationMode
public abstract int getNavigationMode();
public abstract void setNavigationMode(@NavigationMode int mode);
实际上,95%我想要类型别名的情况有点相同:当我得到某种数据库行ID时,通常是long
或String
,这导致必须对类型信息进行编码名称中的变量(例如long folderId
,long messageId
)。但是,StringDef
/ IntDef
注释存在恼人的限制:由于某种原因,它们需要一组预定义的常量,因此您只能描述一组有限的值,这显然不足以数据库标识符。
我想要的是某种类似的注释说@TypeAlias
和Lint检查,所以我可以这样做:
@TypeAlias
@Retention(RetentionPolicy.SOURCE)
public @interface FolderId { }
@FolderId
public long getFolderIdByName(Database db, String name) {
long id = db.foldersTable().findByName(name).getId();
return id;
/*
we might need to suppress the check here
(because we pass long as a @FolderId long),
but that's okay, since we are aware of
what we are doing here, and it's the only
possible injection point for @FolderId
*/
}
public void deleteMessagesIn(Database db, @FolderId long folder) {
// whatever
}
public void deleteMessagesInInbox(Database db) {
deleteMessagesIn(db, 1); // rejected by Lint, trying to pass long as @FolderId long
deleteMessagesIn(db, getFolderIdByName(db, "Inbox")); // ok, passes Lint check
}
我没有看到将类型别名注释限制为一组有限值的任何充分理由,我们可以通过如上所述的抑制来解决它,或者可能通过专门为提供类型别名的函数引入另一个注释来解决它。
更重要的是,这不仅限于Android注释的限制:任何普通的Java应用程序也可以从这些类型的别名中受益,而且我没有设法远程类似地谷歌。我们也可以从使用TYPE_PARAMETER目标中受益,这将使它看起来几乎像真正的类型别名。
所以问题是:
P.S。 @mernst将我指向Checker框架,我设法以相对无痛的方式将其与Android集成:https://github.com/karlicoss/checker-fenum-android-demo
答案 0 :(得分:2)
检查类型别名(typedef)的正确使用的静态分析器是与Fake Enum Checker一起分发的Checker Framework。它已被用于查找Swing和JabRef中的错误。