枚举类型是Java 5中新增特性的一部分,它是一种特殊的数据类型,之所以特殊是因为它既是一种类(class)类型却又比类类型多了些特殊的约束,但是这些约束的存在也造就了枚举类型的简洁性、安全性以及便捷性。
举一个常见的星期一、二、...星期日枚举定义的例子:
package main;
public class HelloEnum {
//通过关键字enum定义枚举类型
enum Day {
MONDAY, TUESDAY, WEDNESDAY,THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.print(Day.MONDAY);
}
}
了解了枚举类型的定义与简单使用后,现在有必要来了解一下枚举类型的基本实现原理:实际上在使用关键字enum创建枚举类型并编译后,编译器会为我们生成一个相关的类,这个类继承了Java API中的java.lang.Enum类,也就是说通过关键字enum创建枚举类型在编译后事实上也是一个类类型而且该类继承自java.lang.Enum类。它已经不再是单纯的常量定义了。
虽然它的使用也很简单,但是在Android开发中官网不推荐使用枚举enums。为什么呢?
占用内存多(Enums often require more than twice as much memory as static constants.)。 Android中当你的App启动后系统会给App单独分配一块内存,App的DEX code、Heap以及运行时的内存分配都会在这块内存中。
总结一下就是:
(1)每一个枚举值都是一个单例对象,在使用它时会增加额外的内存消耗,所以枚举相比与Integer和String会占用更多的内存;
(2)较多的使用Enum会增加DEX文件的大小,会造成运行时更多的IO开销,使我们的应用需要更多的空间。特别是分dex多的大型APP,枚举的初始化很容易导致ANR。
使用注解方案
注解相当于一种标记,在程序中加上了注解就等于为程序加上了某种标记,以后JAVAC编译器,开发工具和其他程序可以用反射来了解你的类以及各种元素上有无任何标记,看你有什么标记,就去干相应的事。
先来总结一下常见的3个注解:@Override,@Deprecated,@SuppressWarnings。
① 注解@Override用在方法上,当我们想重写一个方法时,在方法上加@Override,当我们方法的名字出错时,编译器就会报错。
② 注解@Deprecated,用来表示某个类的属性或方法已经过时,不想别人再用时,在属性和方法上用@Deprecated修饰注解。
③ 注解@SuppressWarnings用来压制程序中出来的警告。
④ 注解@Retention可以用来修饰注解,是注解的注解,称为元注解。
@Retention
下面重点说这个@Retention注解的源码定义和相关内容的解释:
/*
- Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
- DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*/
package java.lang.annotation;
/**
- Indicates how long annotations with the annotated type are to
- be retained. If no Retention annotation is present on
- an annotation type declaration, the retention policy defaults to
- {@code RetentionPolicy.CLASS}.
*
- <p>A Retention meta-annotation has effect only if the
- meta-annotated type is used directly for annotation. It has no
- effect if the meta-annotated type is used as a member type in
- another annotation type.
*
- @author Joshua Bloch
- @since 1.5
- @jls 9.6.3.2 @Retention
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
/**
* Returns the retention policy.
* @return the retention policy
*/
RetentionPolicy value();
}
这个Retention指的是注解的注解,称为元注解。它有一个属性value(),是RetentionPolicy类型的,Enum RetentionPolicy是一个枚举类型,这个枚举决定了Retention注解应该如何去保持,也可理解为Rentention 搭配 RententionPolicy使用。RetentionPolicy有3个值,源码如下:
/*
- Copyright (c) 2003, 2004, Oracle and/or its affiliates. All rights reserved.
- DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*/
package java.lang.annotation;
/**
- Annotation retention policy. The constants of this enumerated type
- describe the various policies for retaining annotations. They are used
- in conjunction with the {@link Retention} meta-annotation type to specify
- how long annotations are to be retained.
*
- @author Joshua Bloch
- @since 1.5
*/
public enum RetentionPolicy {
/**
* Annotations are to be discarded by the compiler.
*/
SOURCE,
/**
* Annotations are to be recorded in the class file by the compiler
* but need not be retained by the VM at run time. This is the default
* behavior.
*/
CLASS,
/**
* Annotations are to be recorded in the class file by the compiler and
* retained by the VM at run time, so they may be read reflectively.
*
* @see java.lang.reflect.AnnotatedElement
*/
RUNTIME
}
分别来解释下这些组合后用法的意思:
用@Retention(RetentionPolicy.CLASS)修饰的注解,表示当程序编译时注解的信息被保留在class文件(字节码文件)中,但不会被虚拟机读取在运行的时候;
用@Retention(RetentionPolicy.SOURCE )修饰的注解,表示注解的信息会被编译器抛弃,不会留在class文件中,注解的信息只会留在源文件中;
用@Retention(RetentionPolicy.RUNTIME )修饰的注解,表示当程序编译时注解的信息被保留在class文件(字节码文件)中,会被虚拟机保留在运行时,所以他们可以用反射的方式读取。RetentionPolicy.RUNTIME 可以让你从JVM中读取Annotation注解的信息,以便在分析程序的时候使用.
@interface
@interface是用来自定义注释(注解)类型(JAVA Annotation)的语法。注释(注解)类型的定义跟定义一个接口相似,我们需要在 interface这个关键字前面加上一个@符号,即@interface。注释中的每一个方法定义了这个注释类型的一个元素,注释中方法的声明中一定不能包含参数,也不能抛出异 常;方法的返回值被限制为简单类型、String、Class、emnus、注释,和这些类型的数组。方法可以有一个缺省值。
@StringDef
/*
- Copyright (C) 2014 The Android Open Source Project
*/
package android.support.annotation;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
/**
- Denotes that the annotated String element, represents a logical
- type and that its value should be one of the explicitly named constants.
- <p>
- Example:
- <pre>
- Retention(SOURCE)
- StringDef({
- POWER_SERVICE,
- WINDOW_SERVICE,
- LAYOUT_INFLATER_SERVICE
- })
- public @interface ServiceName {}
- public static final String POWER_SERVICE = "power";
- public static final String WINDOW_SERVICE = "window";
- public static final String LAYOUT_INFLATER_SERVICE = "layout_inflater";
- ...
- public abstract Object getSystemService(@ServiceName String name);
- </pre>
*/
@Retention(SOURCE)
@Target({ANNOTATION_TYPE})
public @interface StringDef {
/** Defines the allowed constants for this element */
String[] value() default {};
}
@StringDef来代替Enum枚举。它会帮我检测像Enum枚举一样,在编译时期检查变量的赋值情况,但又不像枚举那么耗费内存资源!
下面是源码中给的使用举例:
@Retention(SOURCE)
@StringDef({
POWER_SERVICE,
WINDOW_SERVICE,
LAYOUT_INFLATER_SERVICE
})
public @interface ServiceName {}
public static final String POWER_SERVICE = "power";
public static final String WINDOW_SERVICE = "window";
public static final String LAYOUT_INFLATER_SERVICE = "layout_inflater";
...
public abstract Object getSystemService(@ServiceName String name);
@IntDef
/*
- Copyright (C) 2014 The Android Open Source Project
*/
package android.support.annotation;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
/**
- Denotes that the annotated element of integer type, represents
- a logical type and that its value should be one of the explicitly
- named constants. If the IntDef#flag() attribute is set to true,
- multiple constants can be combined.
- <p>
- Example:
- <pre>
- @Retention(SOURCE)
- @IntDef({NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS})
- 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;
- ...
- public abstract void setNavigationMode(@NavigationMode int mode);
- @NavigationMode
- public abstract int getNavigationMode();
- </pre>
- For a flag, set the flag attribute:
- <pre>
- @IntDef(
- flag = true,
- value = {NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS})
- </pre>
*
- @see LongDef
*/
@Retention(SOURCE)
@Target({ANNOTATION_TYPE})
public @interface IntDef {
/** Defines the allowed constants for this element */
int[] value() default {};
/** Defines whether the constants can be used as a flag, or just as an enum (the default) */
boolean flag() default false;
}
上面源码中给的例子:
@Retention(SOURCE)
@IntDef({NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS})
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;
...
public abstract void setNavigationMode(@NavigationMode int mode);
@NavigationMode
public abstract int getNavigationMode();
// For a flag, set the flag attribute:
IntDef(
flag = true,
value = {NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST,NAVIGATION_MODE_TABS})
在Activity中使用实例:
public class MainActivity extends Activity {
//先定义 常量
public static final int RED = 0;
public static final int GREEN = 1;
public static final int BLUE = 2;
//用 @IntDef "包住" 常量,这里使用@IntDef来代替Enum枚举,也可以使用@StringDef。它会像Enum枚举一样在编译时期检查变量的赋值情况!
//这里是定义包含三种颜色的枚举值的枚举
@IntDef({RED, GREEN, BLUE})
// @Retention 定义注解保留的策略,是默认保留注解
@Retention(RetentionPolicy.SOURCE)
//接口定义 即通过@interface自定义的注解类型
public @interface ColorTypes {}
@ColorTypes int mColor = RED ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setColorType(GREEN);
//声明变量,使用时需要设置一个标记
@ColorTypes int colorType = getColorType();
switch (colorType){
case RED:
break;
case GREEN:
break;
case BLUE:
break;
default:
break;
}
}
//使用上面自定义的注解来注解方法参数
public void setColorType(@ColorTypes int color) {
this.mColor = color;
}
//使用上面自定义的注解来注解这个方法
@ColorTypes
public int getColorType() {
return mColor;
}