Effective Java(Item: 13 to 22)

来源:互联网 发布:淘宝店背景怎么设置 编辑:程序博客网 时间:2024/05/16 05:14

Three:Class and Interfaces
Item 13: Minimize the accessibility of classes and members
The rule is simple: make each class or member as inaccessible as possible. This concept, knows as information hiding or encapsulation.. For members (fields, methods, nested classes and nested interfaces), there are four possible access levels, listed here in order of increasing accessibility:
1).private-The member is accessible only from the top-level class where it is declared;
2).package-private-The member is accessible from any class in the package where it is declared, also known as default access;
3).protected-The member is accessible from subclasses of the class where it is declared;
4).public-The member is accessible from anywhere;
And for minimize the accessibility, you need yo be awared the rules:
1).Instance fields should never be public;
2).Classes with mutable fields are not thread-safe
3).It is wrong for a class to have a public static array field, or an accessor that returns such a field;

Item 14: In public classes, use accessor methods, not public fields
I think you have already know a lot programs like setter and getter, what does that mean?
Remember that if a class is accessible outside its package, provide accessor methods, do not expose fields directly.
In summary, public classes should never expose mutable fields. It is less harmful, though still questionable, for public classes to expose immutable fields. It is, however, sometimes desirable for package-private or private nested classes to expose fields, whether mutable or immutable.
Here is a simple example about Item 13 and Item 14 :

class Point{    /*wrong    public double x;    public double y;    public static final Thing[] VALUES={1,2,3,4};    */    private double x;    private double y;    private static final Integer[] VALUES={1,2,3,4};    public Point(double x, double y){        this.x=x;        this.y=y;    }     public double getX(){return x;}    public double getY(){return y;}    public void setX(double x){this.x=x;}    public void setY(double y){this.y=y;}    public static final Integer[] getValues(){        return VALUES.clone();    }}

Item 15: Minimize mutability
To make a class immutable, follow these five rules:
1).Don’t provide any methods that modify the object’s state;
2).Ensure that the class can’t be executes;
3).Make all fields final;
4).Make all fields private;
5).Ensure exclusive access to any mutable components;
Why would you do that?
1).Immutable objects are simple;
2).Immutable objects are inherently thread-safe;
3).Immutable objects can be shared freely;
4).Immutable objects make great building blocks for other objects;
But you need to be clearly that the only disadvantage of immutable classes is that they require a separate object for each distinct value.
To summarize, resist the urge to write a set method for every get method. Classes should be immutable unless there’s a very good reason to make them mutable. If a class cannot be make immutable, limit its mutability as mush as possible. Trying to make every field final unless there is a compelling reason to it nonfinal.
Example:

public class MinimizeMutability{    public static void main(String[] args){        Complex c = Complex.valueOf(1.1, 2.2);        System.out.println(c);        c=c.add(c);        System.out.println(c);        c=c.subtract(c);        System.out.println(c);    }}final class Complex{    private final double re;    private final double im;    private Complex(double re, double im) {        this.re = re;        this.im = im;    }    public static Complex valueOf(double re, double im) {        return new Complex(re,im);    }    //Accessors with no corresponding mutators    public double realPart() {return re;}    public double imaginaryPart() {return im;}    public Complex add(Complex c) {        return valueOf(re+c.re, im+c.im);    }    public Complex subtract(Complex c) {        return valueOf(re-c.re, im-c.im);    }    public Complex multiply(Complex c) {        return valueOf(re * c.re - im * c.im,                re * c.im + im * c.im);    }    public Complex divide(Complex c) {        double tmp = c.re * c.re + c.im * c.im;        return valueOf((re * c.re + im * c.im) / tmp,                (im * c.re - re * c.im) / tmp);    }    @Override    public boolean equals(Object o) {        if(o == this)            return true;        if(!(o instanceof Complex))            return false;        Complex c = (Complex)o;        return Double.compare(re, c.re) == 0 &&            Double.compare(im, c.im) == 0;    }    @Override    public int hashCode() {        int result = 17 + hashDouble(re);        result = 31 * result + hashDouble(im);        return result;    }    private int hashDouble(double val) {        long longBits = Double.doubleToLongBits(re);        return (int)(longBits ^ (longBits >>> 32));    }    @Override    public String toString() {        return "("+ re + "+" + im + ")";    }}/*Output:(1.1+2.2)(2.2+4.4)(0.0+0.0)*///:~ 

Item 16: Favor composition over inheritance
Inheritance can lead to fragile software and violate encapsulation. To fix the problem that inheritance have bring, A powerful design called composition come out, which make the existing class becomes a component of the new one. Each instance method in the new class invokes the corresponding method on the contained instance of the existing class and return the results. This is knows as forwarding, and the methods in the new class are known as forwarding methods. The resulting class class will be rock soild, with no dependencies on the implementation details of the existing class. Even adding new methods to the existing class will have no impact on the new class.
Of course, inheritance is appropriate only in circumstances where the subclass really is a subtype of the superclass. Inheritance is a “is-a” relationship.
Example:

import java.util.*;public class CompositionVSInheritance {    public static void main(String[] args) {        InstrumentedHashSet<String> s=            new InstrumentedHashSet<String>();        s.addAll(Arrays.asList("Snap", "Crackle", "Pop"));        System.out.println("(Inheritance)AddCount of InstrumentedHashSet is-->"+s.getAddCount());        //composition is flexible        InstrumentedHashSet2<String> s2 =             new InstrumentedHashSet2<String>(new HashSet<String>());        s2.addAll(Arrays.asList("Snap", "Crackle", "Pop"));        System.out.println("(Composition)AddCount of InstrumentedHashSet2 is-->"+s2.getAddCount());        Set<Integer> s3 = new InstrumentedHashSet2<Integer>(new TreeSet<Integer>());        System.out.println("composition is flexible!");    }}//Broken - Inappropriate use of inheritance!class InstrumentedHashSet<E> extends HashSet<E> {    private int addCount = 0;    public InstrumentedHashSet() {    }    public InstrumentedHashSet(int initCap, float loadFactor) {        super(initCap, loadFactor);    }    @Override    public boolean add(E e) {        addCount++;        System.out.println("call me!-->InstrumentedHashSet.add()");        return super.add(e);    }    @Override    public boolean addAll(Collection<? extends E> c) {        addCount += c.size();        System.out.println("call me!-->InstrumentedHashSet.addAll()");        return super.addAll(c);    }    public int getAddCount() {        return addCount;    }}//Wapper class - uses composition in place of inheritanceclass InstrumentedHashSet2<E> extends ForwardingSet<E> {    private int addCount = 0;    public InstrumentedHashSet2(Set<E> s) {        super(s);    }    @Override    public boolean add(E e) {        addCount++;        System.out.println("call me!-->InstrumentedHashSet2.add()");        return super.add(e);    }    @Override    public boolean addAll(Collection<? extends E> c) {        addCount += c.size();        System.out.println("call me!-->InstrumentedHashSet2.addAll()");        return super.addAll(c);    }    public int getAddCount() {        return addCount;    }}//Reusable forwarding classclass ForwardingSet<E> implements Set<E> {    private final Set<E> s;    public ForwardingSet(Set<E> s) {this.s = s; }    public void clear() {s.clear();}    public boolean contains(Object o) {return s.contains(o); }    public boolean isEmpty() {return s.isEmpty(); }    public int size() {return s.size(); }    public Iterator<E> iterator() {return s.iterator(); }    public boolean add(E e)    {        System.out.println("call me!-->ForwardingSet.add()");        return s.add(e);     }    public boolean remove(Object o) {return s.remove(o); }    public boolean containsAll(Collection<?> c)    {return s.containsAll(c); }    public boolean addAll(Collection<? extends E> c)    {        System.out.println("call me!-->ForwardingSet.addAll()");        return s.addAll(c);    }    public boolean removeAll(Collection<?> c)    {return s.removeAll(c); }    public boolean retainAll(Collection<?> c)    {return s.retainAll(c); }    public Object[] toArray() {return s.toArray(); }    public <T> T[] toArray(T[] a) {return s.toArray(a); }    @Override    public boolean equals(Object o) {return s.equals(o); }    @Override    public int hashCode() {return s.hashCode(); }    @Override    public String toString() {return s.toString(); }}/*Output:call me!-->InstrumentedHashSet.addAll()call me!-->InstrumentedHashSet.add()call me!-->InstrumentedHashSet.add()call me!-->InstrumentedHashSet.add()(Inheritance)AddCount of InstrumentedHashSet is-->6call me!-->InstrumentedHashSet2.addAll()call me!-->ForwardingSet.addAll()(Composition)AddCount of InstrumentedHashSet2 is-->3composition is flexible!*///:~

Item 17: Design and document for inheritance or else prohibit it
1).A class which designed and documented for inheritance must document its selff-use of overridable methods;
2).The only way to test a class designed for inheritance is to write subclasses;
3).You must test your class by writing subclasses before you release it;
4).Constructors must not invoke overridable methods;
5).Neither clone nor readObject may invoke an overridable method, directly or indirectly;
6).Designing a class for inheritance places substantial limitation on the class;
7).Prohibiting subclassing in class that are not designed and documented to be safely subclassed, you can declare the class final or add public static factories in place of the constructors;
8).If you feel that you must allow inheritance from such a class, one reasonable approach is to ensure that the class never invokes any of its overridable methods and to document this fact;
Example:

import java.util.Date;public class ProhibitInheritance {    /*     * The overrideMe method is invoked by the Super constructor before     * the Sub constructor has a chance to initialize the date field.     * Note that this program observes a final field in two different states!     */    public static void main(String[] args) {        Sub sub = new Sub();        sub.overrideMe();    }}class Super {    //Broken - constructor invokes an overridable method    public Super() {        overrideMe();    }    public void overrideMe() {        System.out.println("I won't be call for i have be overrided!");    }}final class Sub extends Super {    private final Date date;    public Sub() {        date = new Date();    }    //Overriding method invoked by i constructor    @Override    public void overrideMe() {        System.out.println(date);    }}/*Output:nullMon Oct 10 20:28:14 CST 2016*///:~

Item 18: Prefer inheritance to abstract classes
1).Existing classes can be easily retrofitted to implement a new interface;
2).Interfaces are ideal for defining mixins;
3).Interface allow the construction of nonhierarchical type frameworks;
4).Interfaces enable safe, powerful functionality enhancements via the wrapper class idiom;
5).You can combine the virtues of interfaces and abstract classes by providing an abstract skeletal implementation class to go with each nontrivial interface that you export;
6).It is far easier to evolve an abstract class than an interface;
7).Once an interface is released and widely implemented, it is almost impossible to change;

public class AbstractSkeletal {}//Let's see the primitive code//Abstract classabstract class AbrBase {    abstract void a();    abstract void b();}class Sub1 extends AbrBase {    public void a() {}    public void b() {}} //Interfaceinterface IBase{    public void c();    public void d();}class Sub2 implements IBase {    public void c() {}    public void d() {}}//Now we need to add some method in Abstract class and Interface//Enhance Abstractabstract class EnhanceAbrBase {    abstract void a();    abstract void b();    //add a new method    public void e() {}}class EnhanceSub1 extends EnhanceAbrBase {    public void a() {}    public void b() {}    /*    *Abstract don't need to be changed.    *this is the only adventage compare to interface.    */} //Enhance Interfaceinterface EnhanceIBase{    public void c();    public void d();    //add a new method    public void f();}class EnhanceSub2 implements EnhanceIBase {    public void c() {}    public void d() {}    //Subclass must implement the new method of SuperClass    //And this is not good. We need to fix it.    public void f() {}}//We use skeletal implementation to conjunc Abstract and Interfaceinterface SkeletalIBase {    public void c();    public void d();}abstract class SkeletalAbrBase implements SkeletalIBase {    //primitive    abstract void a();    abstract void b();    //implements the method of SkeletalIBase interface    public void c() {}    public void d() {}}class SkeletalSub extends SkeletalAbrBase {    public void a() {}    public void b() {}}//Now we add some method in Skeletal implementationinterface EnhanceSkeletalIBase {    public void c();    public void d();    //add a new method    public void f();}abstract class EnhanceSkeletalAbrBase implements EnhanceSkeletalIBase {    //primitive    abstract void a();    abstract void b();    //implements the method of EnhanceSkeletalIBase interface    public void c() {}    public void d() {}    //implement the new method    public void f() {}}/** you don't need to change anything*/class EnhanceSkeletalSub extends EnhanceSkeletalAbrBase {    public void a() {}    public void b() {}}

Item 19: Use interface only to define types
People always use constant interface pattern sometimes, which is a poor use of interface. If in a future release the class is modified so that it no longer needs to use the constant, it still must implement the interface to ensure binary compatibility. If a nonfinal class implement a constant interface, all of its subclasses will have their namespaces polluted by the constant in the interface.
In summary, interfaces should be used only to defind typed. They should not be used to export constants.
For example:

public class InterfaceConstant{    public static void main(String[] args) {        System.out.println(PhysicalConstants.AVOGADROS_NUMBER);    }}/*Contants interface antipattern - do not use!interface PhysicalConstants {    static final double AVOGADROS_NUMBER = 6.02214199e26;    static final double BOLTZMANN_CONSTANT = 1.3806503e-23;    static final double ELECTRON_MASS = 9.10938188e-31;}*/class PhysicalConstants {    private PhysicalConstants() {}     static final double AVOGADROS_NUMBER = 6.02214199e26;    static final double BOLTZMANN_CONSTANT = 1.3806503e-23;    static final double ELECTRON_MASS = 9.10938188e-31;}/*Output:6.02214199E26*///:~

Item 20: Prefer class hierarchies to tagged classes
The disadvantage of tagged class:
1).Read-ability is future harmed because multiple implementations are jumbled together in a single class;
2). Memory footprint is increased because instances are burdened with inelevant fields belonging to other flavors;
So we can make a conclusion that tagged class are verbose, error-prrone, and inefficient. A tagged class is just a pallid imitation of a class hierarchy.
The advantage of hierarchies:
1).Hierarchies corrects every shortcoming of tagged classes noted previously;
2).Hierarchies can be make to reflect natural hierarchies relationships among types, allowing for increased flexibility and better compile-time type checking;
Show you the code:

public class TaggedClass {    public static void main(String[] args) {        Figure f = new Figure(4.4);        System.out.println(f.area());        Figure ff = new Figure(2.2, 1.1);        System.out.println(ff.area());    } }class Figure {    enum Shape { RECTANGLE, CIRCLE };    //Tag field - the shape of this figure    final Shape shape;    //These fields are used only if shape is RECTANGLE    double length;    double width;    //This field is used only if shape is CIRCLE    double radius;    //Constructor for circle    Figure(double radius) {        shape = Shape.CIRCLE;        this.radius = radius;    }    //Constructor for rectangle    Figure(double length, double width) {        shape = Shape.RECTANGLE;        this.length = length;        this.width = width;    }    double area() {        switch(shape) {            case RECTANGLE:                return length * width;            case CIRCLE:                return Math.PI * (radius * radius);            default:                throw new RuntimeException();        }    }}/*Output:60.8212337734984062.4200000000000004*///:~public class ClassHierarchies {    public static void main(String[] args) {        Figure f = new Circle(4.4);        System.out.println(f.area());        Figure ff = new Rectangle(2.2, 1.1);        System.out.println(ff.area());    }}abstract class Figure {    abstract double area();}class Circle extends Figure {    final double radius;    Circle(double radius) { this.radius = radius; }    double area() { return Math.PI * (radius * radius); }}class Rectangle extends Figure {    final double length;    final double width;    Rectangle(double length, double width) {        this.length = length;        this.width = width;    }    double area() { return length * width; }}/*Output:60.8212337734984062.4200000000000004*///:~  

Item 21: Use function objects to represent strategies
To summarize, a primary use of function pointers is to implement the Strategy pattern. To implement this pattern in Java, declare an interface to represent the strategy, and a class that implements this interface for each concrete strategy. When a concrete strategy is used only once, it is typically declare and instantiated as an anonymous class. When a concrete strategy is designed for represent use, it is generally implemented as a private static member class and exported in a public static final field whose type is the strategy interface.

Item 22: Favors static member class over nonstatic
There are four kinds of nested classes: static member classes, nonstatic member classes, anonymous classes, and local classes.
1).A static member class is a static member of its enclosing class and obeys the same accessibility rules as other static members.
2).Within instance methods of a nonstatic member class, you can invoke methods on the enclosing instance or obtain a reference to the enclosing instance using the qualified this construct.
3).If you declare a member class that does not require access to an enclosing instance, always put the static modifier in its declaration.
4).One common use of anonymous classes is to create function and process objects.
5).Local classes are the least frequently used of the four kinds nested classes.
6).When you use anonymous or local class, keep it short or readability will ssuffer.
In conclusion, if a nested class needs to be visible outside of a single method or is too long to fit comfortable inside a method, use a member class. If each instance of the member class needs a reference to its enclosing instance, make it nonstatic;
otherwise, make it static. Assuming the classes belongs inside a method, if you need to create instance from only one location and there is a preexisting type that characterizes the class, make it an anonymous class; otherwise, make it a local class.
Example about Item 21 and Item 22:

//implement interface as strategypublic class Comptype implements Comparable<Comptype>{    int i;    int j;    private static int count = 1;    public Comptype(int n1, int n2) {        i = n1;        j = n2;    }    public String toString() {        String result = "[i=" + i + ", j ="+ j +"]";        if(count++ % 3 == 0)            result += "\n";        return result;    }    public int compareTo(Comptype rv) {        return (i < rv.i ? -1 : (i == rv.i ? 0 : 1));    }    private static Random r = new Random(47);    public static Generator<Comptype> generator() {        return new Generator<Comptype>() {            //anonymous class            public Comptype next() {                return new Comptype(r.nextInt(100), r.nextInt(100));            }        };    }    public static void main(String[] args) {        Comptype[] a =            Generated.array(new Comptype[12], generator());        System.out.println("before sorting:");        System.out.println(Arrays.toString(a));        Arrays.sort(a);        System.out.println("after sorting:");        System.out.println(Arrays.toString(a));    }}class Generated {    //static member class    public static <T> T[] array(T[] a, Generator<T> gen) {        return new CollectionData<T>(gen, a.length).toArray();    }}…
0 0
原创粉丝点击