Android开发之枚举(Enum)在实际项目中的应用

2017-09-06 16:00 阅读 815 次 评论 0 条
版权声明:本文著作权归TeachCourse所有,未经许可禁止转载,谢谢支持!
转载请注明出处:http://teachcourse.cn/2482.html

摘要:

谈起枚举(enum),我脑海里立即呈现出一周有七天(Mon、Tues、Wed、Thur、Fri、Sat、Sun)和一年有四季(Spring、Summer、Autumn、Winter)的例子,除此之外,关于什么时候使用枚举枚举支持调用哪些方法如何用类实现枚举的功能以及枚举在实际项目中的应用,我是一无所知,经过一番学习后,本篇文章就从这几方面入手,揭开枚举(enum)的真实面纱,学习枚举(enum)的高级用法。

一、 什么时候使用枚举?

枚举不同于接口,类用class标识,接口用interface标识,而枚举用enum标识,枚举名建议带上Enum后缀,枚举成员名称需要全大写,单词间用下划线隔开。

以一年四季为例,学习枚举的基本用法,创建枚举SeasonEnum,包括成员:SPRINTSUMMERAUTUMNWINTER,代码如下:

public enum SeasonEnum {
    SPRINT,SUMMER,AUTUMN,WINTER
}

调用的方式,代码如下:

    SeasonEnum season=SeasonEnum.AUTUMN
    printDesc(season);

判断两个枚举成员是否相等,用等号(==),代码如下:

    private void printDesc(SeasonEnum season) {
        if (SeasonEnum.SPRINT == season) {
            Log.d(TAG, "printDesc: 春天,春来江水绿如蓝");
        } else if (SeasonEnum.SUMMER == season) {
            Log.d(TAG, "printDesc: 夏天,牧童骑黄牛,歌声振林樾");
        } else if (SeasonEnum.AUTUMN == season) {
            Log.d(TAG, "printDesc: 秋天,夕阳西下,断肠人在天涯");
        } else {
            Log.d(TAG, "printDesc: 冬天,遥知不是雪,为有暗香来");
        }
    }

运行demo,控制台输出:秋天,夕阳西下,断肠人在天涯

到此,我们学会了枚举的基本用法,接下来再看一周七天的例子,制定一周的工作计划,创建枚举WeeklyPlanEnum,包括成员:MONTUESWEDTHURFRISATSUN,代码如下:

/**
 * Created by http://teachcourse.cn on 2017/9/5.
 */
public enum WeeklyPlanEnum {
    MON("周一:了解反编译apk文件的方法"),
    TUES("周二:学习防止反编译的技术——代码混淆、压缩、优化"),
    WED("周三:参考代码混淆官方文档"),
    THUR("周四:学习混淆普通的包名、类名、方法名的方法"),
    FRI("周五:学习混淆Android项目的资源文件、属性文件"),
    SAT("周六:休息、睡懒觉"),
    SUN("周日:打球");
    private String plan;

    WeeklyPlanEnum(String plan) {
        this.plan = plan;
    }

    public String getPlan() {
        return plan;
    }
}

因为我们创建了带参数的构造方法,所以可以往MON里面传入参数(也必须传参),制定好一周的工作计划,就老老实实按计划行事,调用getPlan()方法,查看具体的工作内容,代码如下:

    WeeklyPlanEnum friday=WeeklyPlanEnum.FRI;
    String plan=friday.getPlan();
    Log.d(TAG, "onCreate: "+plan);

运行demo,控制台输出:周五:学习混淆Android项目的资源文件、属性文件

从上面两个例子,你是否可以总结出一些共同的特点,是否明白什么时候使用枚举?如果还是不明白,我们再看一个例子:本学期,计算机科学与技术专业学院已经评选出三名优秀学生干部,学生信息包括:学号(num)、姓名(name)、专业(profession),现在需要根据学号查询学生的其他信息,使用枚举实现,代码如下:

/**
 * Created by http://teachcourse.cn on 2017/9/5.
 */
public enum ExcellentStudentEnum {
    ZHAOYUN(2011110924, "赵云", "网络工程方向"),
    ZHANGFEI(2011110925, "张飞", "信息工程方向"),
    LIUBEI(2011110926, "刘备", "数字媒体方向"),
    NONE(0, "匿名", "你猜我学什么专业?");
    private int num;
    private String name;
    private String profession;

    ExcellentStudentEnum(int num, String name, String profession) {
        this.num = num;
        this.name = name;
        this.profession = profession;
    }

    public int getNum() {
        return num;
    }

    public String getName() {
        return name;
    }

    public String getProfession() {
        return profession;
    }

    public static String query(int num){
        for (ExcellentStudentEnum student:values()){
            if(student.getNum()==num)
                return "姓名:"+student.getName()+","+
                        "专业:"+student.getProfession();
        }
        return "姓名:"+NONE.getName()+","+
                "专业:"+NONE.getProfession();
    }
}

调用如下方法,查询三好学生的其他信息,代码如下:

Log.d(TAG, "onCreate: "+ ExcellentStudentEnum.query(2011110924));
Log.d(TAG, "onCreate: "+ ExcellentStudentEnum.query(2011110927));

运行demo,控制台打印出:

姓名:赵云,专业:网络工程方向
姓名:匿名,专业:你猜我学什么专业?

从上面三个例子,可以总结:当你需要在一个可选区域内,选择某一个选项,获取该选项对应的属性或方法,可以考虑用枚举

例子一:一年有四个季节,是一个可选区域,可以选择其中一项,然后获取描述该季节的诗句。

例子二:一周有七天,是一个可选区域,选择其中一天,获取当天的工作安排。

例子三:评选出的三个优秀学生,是一个可选区域,可以根据学号查询某个学生是否是优秀学生干部,并返回学生的其他信息。

二、枚举支持调用哪些方法?

查看枚举支持调用哪些方法,在Android Studio快速按下两下shift,查找Enum类源码

所有使用enum标识的,统一继承自Enum抽象类,也就继承Enum类的方法和属性,枚举支持调用的方法除了继承的方法外,还可以调用添加的方法。

查看JDK 1.8包的Enum源码,如下图:

枚举Android开发用法

绿色符合标识public修饰的方法,表示Enum的子类可以调用的方法,现在来学习每个继承方法的作用,以第三个例子作为测试,看到Enum抽象类的构造方法,代码如下:

    protected Enum(String name, int ordinal) {
        this.name = name;
        this.ordinal = ordinal;
    }

开发者不能在外部包中调用protected修饰的构造方法,所以可以猜测它可能在程序内部调用,实际上代码被编译器执行,为响应我们用enum标识的枚举类型时调用Enum(String,int)构造方法,而我们声明的成员:ZHAOYUNZHANGFEILIUBEI,将被以字符串name的形式传入,ordinal从0开始依次分配,其他方法的使用情况如下:

  • name(),返回枚举常量的名字,测试代码如下:

    Log.d(TAG, "test: "+ExcellentStudentEnum.LIUBEI.name());
    

    运行demo,控制台打印:

    D/SeasonInfoActivity: test: LIUBEI
    
  • ordinal(),返回枚举序数,测试代码如下:

    Log.d(TAG, "test: "+ExcellentStudentEnum.LIUBEI.ordinal());
    

    运行demo,控制台打印:

    D/SeasonInfoActivity: test: 2
    
  • toString(),返回枚举常量的名字,测试代码如下:

    Log.d(TAG, "test: "+ExcellentStudentEnum.LIUBEI.toString());
    

    运行demo,控制台打印:

    D/SeasonInfoActivity: test: LIUBEI
    
  • equals(Object other),比较传入的对象是否等于当前的枚举,测试代码如下:

    Log.d(TAG, "test: "+ExcellentStudentEnum.LIUBEI.equals("LIUBEI"));
    Log.d(TAG, "test: "+ExcellentStudentEnum.LIUBEI.equals(ExcellentStudentEnum.LIUBEI));
    

    运行demo,控制台打印:

    D/SeasonInfoActivity: test: false
    D/SeasonInfoActivity: test: true
    
  • hashCode(),返回当前枚举常量的哈希码,测试代码如下:

    Log.d(TAG, "test: "+ExcellentStudentEnum.LIUBEI.hashCode());
    

    运行demo,控制台打印:

    D/SeasonInfoActivity: test: -2049136912
    
  • compareTo(E o),比较当前枚举对象和传入的对象的顺序对象,返回一个负整数、0或正整数

    Log.d(TAG, "test: "+ExcellentStudentEnum.LIUBEI.compareTo(ExcellentStudentEnum.ZHAOYUN));
    Log.d(TAG, "test: "+ExcellentStudentEnum.ZHAOYUN.compareTo(ExcellentStudentEnum.ZHAOYUN));
    Log.d(TAG, "test: "+ExcellentStudentEnum.ZHAOYUN.compareTo(ExcellentStudentEnum.ZHANGFEI));
    

    运行demo,控制台打印:

    D/SeasonInfoActivity: test: 2
    D/SeasonInfoActivity: test: 0
    D/SeasonInfoActivity: test: -1
    
  • getDeclaringClass(),返回enum标识的枚举的Class名称,测试代码如下:

    Log.d(TAG, "test: "+ExcellentStudentEnum.LIUBEI.getDeclaringClass());
    

    运行demo,控制台打印:

    D/SeasonInfoActivity: test: class cn.teachcourse.enums.original.ExcellentStudentEnum
    
  • valueOf(Class<T>,String), 这是一个类方法,返回枚举常量的名字,测试代码如下:

    Log.d(TAG, "test: "+ExcellentStudentEnum.valueOf(ExcellentStudentEnum.LIUBEI.getDeclaringClass(),"LIUBEI"));
    Log.d(TAG, "test: "+ExcellentStudentEnum.valueOf(ExcellentStudentEnum.LIUBEI.getDeclaringClass(),"ZHANGFEI"));
    Log.d(TAG, "test: "+ExcellentStudentEnum.valueOf(ExcellentStudentEnum.LIUBEI.getDeclaringClass(),"ZHAOYUN"));
    

    运行demo,控制台打印:

    D/SeasonInfoActivity: test: LIUBEI
    D/SeasonInfoActivity: test: ZHANGFEI
    D/SeasonInfoActivity: test: ZHAOYUN
    
  • values(),这是一个隐藏起来的类方法,因为并不继承Enum类,同时在enum标识的枚举,有没有声明该方法,却可以使用,所以说values()是一个隐藏的类方法,还是很合适的,测试代码如下:

    for (ExcellentStudentEnum student : ExcellentStudentEnum.values()) {
      Log.d(TAG, "test: " +student.getProfession());
    }
    

    运行demo,控制台打印:

    D/SeasonInfoActivity: test: 网络工程方向
    D/SeasonInfoActivity: test: 信息工程方向
    D/SeasonInfoActivity: test: 数字媒体方向
    D/SeasonInfoActivity: test: 你猜我学什么专业?
    
  • valueOf(String),这是一个隐藏起来的类方法,返回指定枚举常量名字的枚举对象,测试代码如下:

    Log.d(TAG, "test: "+ExcellentStudentEnum.valueOf("LIUBEI").getNum());
    Log.d(TAG, "test: "+ExcellentStudentEnum.valueOf("ZHAOYUN").getProfession());
    

    运行demo,控制台打印:

    D/SeasonInfoActivity: test: 2011110926
    D/SeasonInfoActivity: test: 网络工程方向
    

到此,我们完成enum标识的枚举支持调用方法的学习,其中包括继承自Enum抽象类的1-8个方法,以及隐藏的两个类方法values()valueOf(String),对枚举的用法有了更深的认识。

三、 如何用类实现枚举的功能?

阅读Enum抽象类的构造方法说明,我们知道了编译器在编译期间,读取enum关键字标识的枚举,内部调用Enum构造方法,将成员名称name以字符串的形式传入,ordinal从0开始分配序数,最终转换普通的类。

结合TeachCourse个人的经验,枚举可以说是一个固定的模板,编译器负责转换,转换的过程在编译器内部发生,我们无法觉察和看到,更增加了枚举的神秘感,从本质上来说,枚举也是一个类,接下来我们用第三个例子测试,用类实现枚举的功能,代码如下:

/**
 * Created by http://teachcourse.cn on 2017/9/5.
 */
public class ExcellentStudent {
    public static final ExcellentStudent ZHAOYUN = new ExcellentStudent(2011110924, "赵云", "网络工程方向");
    public static final ExcellentStudent ZHANGFEI = new ExcellentStudent(2011110925, "张飞", "信息工程方向");
    public static final ExcellentStudent LIUBEI = new ExcellentStudent(2011110926, "刘备", "数字媒体方向");
    public static final ExcellentStudent NONE = new ExcellentStudent(0, "匿名", "你猜我学什么专业?");

    private int num;
    private String name;
    private String profession;

    ExcellentStudent(int num, String name, String profession) {
        this.num = num;
        this.name = name;
        this.profession = profession;
    }

    public int getNum() {
        return num;
    }

    public String getName() {
        return name;
    }

    public String getProfession() {
        return profession;
    }

    public String query(int num) {
        for (ExcellentStudent student : values()) {
            if (student.getNum() == num)
                return "姓名:" + student.getName() + "," +
                        "专业:" + student.getProfession();
        }
        return "姓名:" + NONE.getName() + "," +
                "专业:" + NONE.getProfession();
    }

    public static ExcellentStudent[] values() {
        return new ExcellentStudent[]{ZHAOYUN, ZHANGFEI, LIUBEI,NONE};
    }

    public static ExcellentStudent valueOf(String name) {
        for (ExcellentStudent student : values()) {
            if (name.equals(student.getName()))
                return student;
        }
        return NONE;
    }
}

除了没有继承Enum抽象类的方法外,ExcellentStudent实现了枚举自身的其他方法,同时添加两个类方法::values()valueOf(String),最终实现的功能和枚举差不多哈哈。

调用ExcellentStudent的方法过程如下所示:

/*1、测试values()方法,返回当前类成员属性*/
for (ExcellentStudent student:ExcellentStudent.values()) {
    Log.d(TAG, "testExcellent: " + student.getProfession());
}
/*2、测试valueOf(),返回指定名称的实例对象*/
Log.d(TAG, "testExcellent: "+ExcellentStudent.valueOf("赵云").getNum());

运行demo,控制台打印:

D/SeasonInfoActivity: testExcellent: 网络工程方向
D/SeasonInfoActivity: testExcellent: 信息工程方向
D/SeasonInfoActivity: testExcellent: 数字媒体方向
D/SeasonInfoActivity: testExcellent: 你猜我学什么专业?
D/SeasonInfoActivity: testExcellent: 2011110924

到此,完成了用类实现枚举的功能,对比了之后,你会发现枚举的简单、方便。

四、 枚举在实际项目中的应用

我们知道在一个可选区域内选择其中一个成员,或者从可选区域查询符合的一个成员,这个时候考虑使用枚举,阅读一些开源项目源码,多少会发现枚举的影子,比如图片加载框架:universal-image-loader,在ImageDownloader这个接口内声明了一个枚举Scheme,源码如下:

...
    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 + "://";
        }

        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());
        }
    }
...

该枚举列举了universal-image-loader支持加载的几种类型图片,以http开头的、https开头的、file开头的、content开头的和assets开头的,添加的ofUri(String),用于查询当前传入的图片地址是否属于可选区域内的一种,将加深我们对枚举的理解、使用。

枚举在实际项目中应用的例子,还有很多,比如TeachCourse项目中引用的友盟分享功能:SHARE_MEDIA

public enum SHARE_MEDIA {
    QQ("qq") {
        public boolean isSupportAuthorization() {
            return true;
        }
        public int getReqCode() {
            return 5658;
        }
    },
    WEIXIN("weixin") {
        public int getReqCode() {
            return 10086;
        }

        public boolean isSupportAuthorization() {
            return true;
        }
    };

    private String a;

    private SHARE_MEDIA(String var3) {
        this.a = var3;
    }

    public boolean isSupportAuthorization() {
        return false;
    }

    public int getReqCode() {
        return 0;
    }

}

网易即时通信demo Tab导航:MainTab

public enum MainTab {
    RECENT_CONTACTS(0, ReminderId.SESSION, SessionListFragment.class, R.string.main_tab_session, R.layout.activity_session_list),
    CONTACT(1, ReminderId.CONTACT, ContactListFragment.class, R.string.main_tab_contact, R.layout.activity_contacts_list),
    CHAT_ROOM(2, ReminderId.INVALID, ChatRoomListFragment.class, R.string.chat_room, R.layout.activity_chat_room_tab);

    public final int tabIndex;
    public final int reminderId;
    public final Class<? extends Fragment> clazz;
    public final int resId;
    public final int fragmentId;
    public final int layoutId;

    MainTab(int index, int reminderId, Class<? extends Fragment> clazz,
            int resId, int layoutId) {
        this.tabIndex = index;
        this.reminderId = reminderId;
        this.clazz = clazz;
        this.resId = resId;
        this.layoutId = layoutId;
        this.fragmentId = index;
    }

    public static final MainTab fromReminderId(int reminderId) {
        for (MainTab value : MainTab.values()) {
            if (value.reminderId == reminderId) {
                return value;
            }
        }

        return null;
    }

    public static final MainTab fromTabIndex(int tabIndex) {
        for (MainTab value : MainTab.values()) {
            if (value.tabIndex == tabIndex) {
                return value;
            }
        }

        return null;
    }
}

阅读源码后,你会发现枚举在项目中的应用涉及的用法基本一样,都包含在前面的例子中。

五、总结

本文从什么时候使用枚举、枚举支持调用的方法、用类实现枚举的功能和枚举在实际项目的应用四个方面,深入理解枚举的用法,加深了对枚举的认识,demo包含多个枚举的例子,可以方便下载阅读。

关注公众号 扫一扫二维码,加我QQ

如果文章对你有帮助,欢迎点击上方按钮关注作者

来源:TeachCourse每周一次,深入学习Android教程,关注(QQ1589359239或公众号TeachCourse)
转载请注明出处:http://teachcourse.cn/2482.html

资源分享

枚举demo源码下载
ViewPager+FragmentPagerAdapter实现简单新闻客户端 ViewPager+FragmentPagerAd
WebView加载HTML5百度地图空白问题 WebView加载HTML5百度地图
Window/Linux下Genymotion快捷键大全 Window/Linux下Genymotion快捷
浅谈RoboVM 浅谈RoboVM

发表评论

呲牙 憨笑 坏笑 偷笑 色 微笑 抓狂 睡觉 酷 流汗 鼓掌 大哭 可怜 疑问 晕 惊讶 得意 尴尬 发怒 奋斗 衰 骷髅 啤酒 吃饭 礼物 强 弱 握手 OK NO 勾引 拳头 差劲 爱你

表情