Effective Java(优先考虑类型安全的异构容器、用enum代替int常量)

1、优先考虑类型安全的异构容器

“异构”的英文heterogeneous意为多种多样的,书中所举的例子我认为非常有参考价值,仔细品味。

import java.util.HashMap;
import java.util.Map;

/**
 * 异构容器
 * Created by zzy on 9/25/17.
 */
public class Favorites {
    private Map, Object> favorites = new HashMap, Object>();

    public  void putFavorite(Class type, T instance) {
        if (type == null) {
            throw new NullPointerException();
        }
        favorites.put(type, instance);
    }

    public  T getFavorite(Class type) {
        return type.cast(favorites.get(type));
    }
}
/**
 * Created by zzy on 9/25/17.
 */
public class Main {

    public static void main(String[] args) {
        Favorites f = new Favorites();
        f.putFavorite(String.class, "Java");
        f.putFavorite(Integer.class, 0xcafebabe);
        f.putFavorite(Class.class, Favorites.class);
        String favoriteString = f.getFavorite(String.class);
        Integer favoriteInteger = f.getFavorite(Integer.class);
        Class favoriteClass = f.getFavorite(Class.class);
        System.out.printf("%s %x %s", favoriteString, favoriteInteger, favoriteClass.getName());
    }
}

Favorite类使用起来有点Map的感觉,putFavorite方法就类似Map.put,或者说用Map不就能实现吗?例如:

import java.util.HashMap;
import java.util.Map;

/**
 * Created by zzy on 9/25/17.
 */
public class Main {

    public static void main(String[] args) {
        Map, Object> map = new HashMap, Object >();
        map.put(String.class, "Java");
        map.put(Integer.class, 122);
        System.out.println(map.get(String.class));
        System.out.println(map.get(Integer.class));
    }
}

能运行和上面结果一致,但问题就在于以下代码:

import java.util.HashMap;
import java.util.Map;

/**
 * Created by zzy on 9/25/17.
 */
public class Main {

    public static void main(String[] args) {
        Map, Object> map = new HashMap, Object >();
        map.put(String.class, "Java");
        map.put(Integer.class, 122);

        Object str = map.get(String.class);    //Integer str = (Integer) map.get(String.class);


        Object in = map.get(Integer.class);

    }
}

根据键取出来的值是Object,也就是说这是很危险的一件事情,如果代码写成上面注释那样的话在编译时是无法判断的,只有在运行时才会抛出异常。记住,能在编译时检查就在编译时检查而不要等到真正运行起来才做检查,这也就是上面Favorite所带来的好处,它是类型安全的,同时它也是异构的,这个例子值得细细品味。
异构容器和map的区别如下:

Effective Java(优先考虑类型安全的异构容器、用enum代替int常量)_第1张图片

2、用enum代替int常量

 对于枚举类型可能并“不常用”,之所以打引号是因为,“不常用”并不是它不好用,而是因为某项原因例如项目外包或者某个项目不需要自己来维护等等,只是追求快速地实现功能。实际上枚举类型对于强化项目代码的结构和规整很有帮助,是一个必不可少所需要强掌握的技能。

  对于使用过枚举的来说,可能最常见的用的比较多的地方就是错误码了,例如:

能运行和上面结果一致,但问题就在于以下代码:

/**
 * 枚举类型错误码
 * Created by zzy on 9/25/17.
 */
public enum ErrorCode {
    FAILURE(0, "操作失败"),
    SUCCESS(1, "操作成功");

    private int code;
    private String msg;
    ErrorCode(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
}
/**
 * Created by zzy on 9/25/17.
 */
public class Main {

    public static void main(String[] args) throws InterruptedException {
        System.out.println(ErrorCode.SUCCESS.getCode() + ErrorCode.SUCCESS.getMsg());
    }
}

这可能是枚举使用比较常见的一种用法。实际上枚举还有其它一些比较“高级”的用法,我们不妨从书中举例来一一说明。首先用枚举来实现加减乘除四种操作:

/**
 * 加减乘除枚举
 * Created by zzy on 9/25/17.
 */
public enum Operation {
    PLUS, MINUS, TIMES, DIVIDE;

    double apply(double x, double y) {
        switch (this) {
            case PLUS: return x + y;
            case MINUS: return x - y;
            case TIMES: return x * y;
            case DIVIDE: return x / y;
        }
        throw new AssertionError("Unknow op:" + this);
    }
}


/**
 * Created by yulinfeng on 8/17/17.
 */
public class Main {

    public static void main(String[] args) {
        double x = 1.1;
        double y = 2.2;
        double result = Operation.PLUS.apply(x, y);
        System.out.println(result);
    }
}

试想如果还需要新增另外一种操作的时候却忘了新增case怎么办?编译时编译器并不会给出任何提示,同样的功能考虑以下实现能很好的避免这种遗忘新增case的情况:

/**
 * 加减乘除枚举
 * Created by yulinfeng on 8/20/17.
 */
public enum Operation {
    PLUS {
        double apply(double x, double y) {
            return x + y;
        }
    },
    MIUS {
        double apply(double x, double y) {
            return x - y;
        }
    },
    TIMES {
        double apply(double x, double y) {
            return x * y;
        }
    },
    DEVIDE {
        double apply(double x, double y) {
            return x / y;
        }
    };

    abstract double apply(double x, double y);
}

如果我们想要新增XXX操作,如果不实现apply方法则不会编译通过,这种方法很巧妙的躲过了因为人为失误带来的隐患。

  枚举确实是一种比较神奇的类型,它不需要你new一个实例,并且在枚举天生就是不可变的,因此所有的域应该为final。一定记住像错误码或者一组固定常量的时候就要优先考虑使用枚举,而不是定义int常量甚至直接将字符串硬编码到代码中。

你可能感兴趣的:(Java)