Android基礎進階之EffectJava翻譯系列(第三章:類和接口)

3. 類和接口

類和接口是Java編程的核心

Item13 最小化類和成員變量的訪問權限

信息隱藏與封裝是程序設計的基本原則
通用經驗是能讓類或者變量不可訪問就讓它不可訪問
四種訪問權限:

  • private
    只在它聲明的地方可用
  • package-private
    同一個包內可用
  • protected
    同一個包或子類可用
  • public
    任何地方可用

公共類不應包含公共字段。確保static final引用的對象是不可變的。
絕不可以用private static final來聲明一個數組對象

Item14 用公共方法提供公共字段
//bad
public class Point {
  public int x;
  public int y;
}

//good
class Point {
    private double x;
    private double y;
    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; }
}
Item15 最小化可變性

對象始終不變的類比可變的更安全和好用,如String
我們設計不可變對象時應遵循如下準則:

  • 不要提供任何方法來改變對象的狀態
  • 確保類不可繼承
  • 所有的字段使用private final修飾
  • 確保不要讓類中的可變對象訪問,使用深拷貝替代
    如果一個類確實可變,也要保證其它地方儘可能不變,TimerTask是很好的例子
Item16 組合和繼承,優先使用組合

繼承雖然有很多好處,但是違背了封裝性,暴露了父類的實現細節

Item17 專用於繼承的設計,否則禁止繼承

未能理解,後續補充

Item18 接口和抽象類之間傾向於接口

Java提供了兩種機制來提供多個類型定義的實現:接口和抽象類

最明顯的區別在於抽象類允許某些方法的實現,更重要的區別在於一個類使用了抽象類的方式來定義,則這個類必須是抽象類的子類.因爲Java只允許單一繼承,因此對抽象類的這種限制嚴重影響了它們作爲類型定義的使用

  • 已經存在的類可以很容易的實現一個新的接口
    比如實現Comparable接口
  • 接口是定義混合器的理想選擇
    比如爲一個主要類型的類添加比較方法,可通過實現Comparable接口實現實例之間的排序

有一個特例是抽象類比接口更加易用,如果注重程序的易用性而不是靈活性則可以考慮抽象類

Item19 僅使用接口定義類型

當一個類實現了一個接口,這個接口是爲類的實例服務的而不是爲了其他目的

所以有一種常量接口儘量不要使用

//bad
public interface PhysicalConstants {
    // Avogadro's number (1/mol)
    static final double AVOGADROS_NUMBER = 6.02214199e23;
    // Boltzmann constant (J/K)
    static final double BOLTZMANN_CONSTANT = 1.3806503e-23;
    // Mass of the electron (kg)
    static final double ELECTRON_MASS = 9.10938188e-31;
}

如果有這種常量需要使用,可以把它定義在與之關聯的類裏面

Item20 用繼承來替代複合類(原單詞: tagged classes)

偶爾會遇到一個類的實例包含了多種不同的類別,如下

// bad Tagged class - vastly inferior to a class hierarchy!
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 AssertionError();
        }
    }
}  

優化如下

//Class hierarchy replacement for a tagged class
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; }
} 
Item21 使用函數對象表示策略

直譯過來有點不好理解,大意是對於多個類都要使用的策略(或用方法表示),採用接口來實現

如一個類需要比較方法,另一個類也需要,則抽象出一個Comparable接口來給各個類通用

Item22 嵌套類的使用

有四種嵌套類,分別是靜態成員類、非靜態成員類、匿名內部類和本地類
除了第一個都是內部類

  • 靜態成員類
    可以訪問外部類的私有屬性
  • 非靜態成員類
    同靜態成員類,區別在於沒有static修飾
public class MySet<E> extends AbstractSet<E> {
    public Iterator<E> iterator() {
        return new MyIterator();
    }
    //典型用法
    private class MyIterator implements Iterator<E> {
    ...
    }
}
  • 匿名內部類
    不用聲明,常用於監聽器,隨用隨銷
  • 本地類
    略 未能理解,後續補充,歡迎留言增益知識

如果成員類需要外部類的引用,則使用非靜態成員類,否則使用靜態成員類


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章