日常java编程中,常见用一些静态常量表示一些状态码,特殊含义的标志等,例如:
1 2 3 4 5 public class A { public static final int STATUS_SUCCESS = 1 ; public static final int STATUS_FAIL = 2 ; public static final int STATUS_CANCEL = 3 ; }
这样使用,虽然看上去很好,但是有不少隐形的问题:
类型不安全;
必须确保int范围;
如果打印的话,只能看到int的值,查看含义的话,需要人工比对
其实这种情况,java的Enum往往是更好的选择。 现在结合Android开源图片下载项目Android-Universal-Image-Loader ,我们来简略分析一下Enum的用法。 Enum的基本使用如下:
1 2 3 public Enum Scheme { HTTP, HTTPS, FILE, CONTENT, ASSETS, DRAWABLE, UNKNOWN; }
使用Enum后,立竿见影的好处是使用switch很方便:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public InputStream getStream (String imageUri, Object extra) throws IOException { switch (Scheme.ofUri(imageUri)) { case HTTP: case HTTPS: return getStreamFromNetwork(imageUri, extra); case FILE: return getStreamFromFile(imageUri, extra); case CONTENT: return getStreamFromContent(imageUri, extra); case ASSETS: return getStreamFromAssets(imageUri, extra); case DRAWABLE: return getStreamFromDrawable(imageUri, extra); case UNKNOWN: default : return getStreamFromOtherSource(imageUri, extra); } }
而且这个时候,如果把变量的值打印出来的话,会打印Enum的内容,而不是int整型,非常直观:
1 2 3 4 5 6 Scheme scheme = Scheme.HTTPS; System.out.println(s); * HTTPS */
但实际上,Enum的使用方式远非如此简单,Enum就是一个类 ,可以包含变量,可以包含函数,当然,也有自己的构造函数。 首先,Enum有两个静态方法:
1 2 3 4 values() 获取枚举类型的所有枚举常量 valueOf(Class<T> EnumType, String name) 返回带指定名称的指定枚举类型的枚举常量。
使用方法以最原始的Enum举例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public Enum Scheme { HTTP, HTTPS, FILE, CONTENT, ASSETS, DRAWABLE, UNKNOWN; } for (Scheme scheme : Scheme.values()) { System.out.print(scheme); System.out.print(" " ); } * HTTP HTTPS FILE CONTENT ASSETS DRAWABLE UNKNOWN */ System.out.println(Scheme.valueOf(Scheme.class, "HTTP" )); * HTTP */
这个时候,我们做一些小小的改动,去为Enum添加一个变量,同时重写toString方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public Enum Scheme { HTTP("http" ), HTTPS("https" ), FILE("file" ), CONTENT("content" ), ASSETS("assets" ), DRAWABLE("drawable" ), UNKNOWN( "unknown" ); private String scheme; Scheme(String s) { scheme = s; } @Override public String toString () { return scheme; } }
这个时候,我们打印一下看看:
1 2 3 4 5 6 7 for (Scheme scheme : Scheme.values()) { System.out.print(scheme); System.out.print(" " ); } * http https file content assets drawable unknown */
可以看到,我们在Enum中的变量可以被Enum中的方法使用。我就说嘛,枚举就是一个类。 现在,让我们仔细看一下Android-Universal-Image-Loader 中的Enum定义:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 public Enum Scheme { HTTP("http" ), HTTPS("https" ), FILE("file" ), CONTENT("content" ), ASSETS("assets" ), DRAWABLE("drawable" ), UNKNOWN("" ); private String scheme; private String uriPrefix; Scheme(String scheme) { this .scheme = scheme; uriPrefix = scheme + "://" ; } * Defines scheme of incoming URI * * @param uri URI for scheme detection * @return Scheme of incoming URI */ public static Scheme ofUri (String uri) { if (uri != null ) { for (Scheme s : values()) { if (s.belongsTo(uri)) { return s; } } } return UNKNOWN; } private boolean belongsTo (String uri) { return uri.toLowerCase(Locale.US).startsWith(uriPrefix); } public String wrap (String path) { return uriPrefix + path; } public String crop (String uri) { if (!belongsTo(uri)) { throw new IllegalArgumentException(String.format("URI [%1$s] doesn't have expected scheme [%2$s]" , uri, scheme)); } return uri.substring(uriPrefix.length()); } }
我们看到,这里面针对协议的Enum,除了定义基本的Enum类型,还提供了根据url去判断是哪一种Enum的静态方法。正因为有这个静态方法,所以,在项目中有了如下优雅的用法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 switch (Scheme.ofUri(imageUri)) { case HTTP: case HTTPS: return getStreamFromNetwork(imageUri, extra); case FILE: return getStreamFromFile(imageUri, extra); case CONTENT: return getStreamFromContent(imageUri, extra); case ASSETS: return getStreamFromAssets(imageUri, extra); case DRAWABLE: return getStreamFromDrawable(imageUri, extra); case UNKNOWN: default : return getStreamFromOtherSource(imageUri, extra); }
Enum那么厉害,那在我们的Android开发中要注意啥呢?要注意的就是,尽量不用Enum 。 为啥??? 早期谷歌语焉不详的提出,enum使用在Android中会产生额外编译开销,后来又语焉不详的把在《性能建议》中不建议使用Enum删除了。个人理解,可能早期的编译器,对Enum的编译不友好,毕竟,Android是自己的一套编译器,而不是java那一套,后来不断改进不断改进,这个问题缓和了。但毕竟Android开发还要考虑早期用户,所以,这里,并不建议大量使用Enum在我们的项目中。 关于这点,可以参考:Why doesn’t Android use more enums? Why was “Avoid Enums Where You Only Need Ints” removed from Android’s performance tips?