Effective Java(Item: 13 to 22)

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 inheritance
class 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 class
class 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-->6
call me!-->InstrumentedHashSet2.addAll()
call me!-->ForwardingSet.addAll()
(Composition)AddCount of InstrumentedHashSet2 is-->3
composition 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:
null
Mon 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 class
abstract class AbrBase {
    abstract void a();
    abstract void b();
}

class Sub1 extends AbrBase {
    public void a() {}
    public void b() {}
} 

//Interface
interface 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 Abstract
abstract 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 Interface
interface 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 Interface
interface 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 implementation
interface 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.821233773498406
2.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.821233773498406
2.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 strategy
public 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值