2009年5月17日 星期日

Practical Java Programming Language Guide-第六章 類別與介面

這個章節有java基本觀念的應該都熟到不行再熟了
我簡單整理一下覺得不動要的就自動跳過了

case 59:運用insterface支持多重繼承
java其實是可以用多重繼承的,只要使用interface就可以達到類似效果
這點在重構(Refacorting)設計模式(Design Patterns)都有相關手法

interface A{...}
class B implements A{}

implements這個關鍵字可以看作extends的一種變體,他會讓B成為A的子類別
而interface則可以看作class一種變體

A a=new B();//這是可以的


case 60:避免interface中的函式衝突

interface A{
int f();
}
interface B{
void f();
}

class C implements A,B{

}

上述例子是編譯不過的
但是可以用回傳一致化或是改名子來解決

interface A{
int f();
}
interface B{
int f();
}

class C implements A,B{

}

如果無法修改原碼的時候,可以用繼承interface或是用adapter這個Desing Patterns解決

case 61:如需提供部分實作(partial implementation)請使用abstract classes
interface強迫必須實作所有他放出來的函式,但是有時候不希望每個介面都實作
就可以使用abstract class

abstract class Foo{
abstract void f1();//有abstract修飾 一定得實作複寫
void f2(){...}//可以不用複寫
}


case 62:區分interface,abstract class和concrete class



一種契約(contract)表示,不代實作
噹想使用單一或多個interface繼承,或為了確定maker interface
public函式和public static final常數
不允許實作
特性
interfaceabstract classconcrete class
描述一種契約(contract)表示,帶有部分實作一種具象實作,經常是某個interface或abstract class的實作
使用時機當打算提供部分實作當打算提供完整的具象實作
內容限制無任何限制無任何限制
實作允許部分實作要求完全實作








總結
支援多重繼承支援抽象函式允許實作允許創劍實體允許部分實作
interfaceYYaNNN
abstract classNYYNY
concrete class NNYYN

a:interface所有函式都暗自宣告成abstract

case 63:審慎定義和實作immutable classes
有些類別希望生存期間不得修改其值,這種類別一旦件夠好就不再變化
他有以下規範

  1. 將class中所有資料宣告成private

  2. 只提供取值(getter)函式而不提供設值(setter)函式

  3. 宣告class為final

  4. 從獲取器回傳reference to mutable object之前先clone那些物件

  5. 將傳遞給建構式之reference to mutable object先clone一份

  6. 在建構式之中就設定好所有資料


舉個例子

final class PinNumber{//使用final防止繼承
private StringBuffer owner;
orivate int acc;
PinNumber(StringBuffer owe,int ac){
owner=owe;
acc=ac;
//物件所有值在建構時就設定好
}
//只提供取值函式
public StringBuffer getOwner{//getter
return owner
}
....
}


case 64:欲傳遞或接收mutable object(可變物件)之object reference時請實施clone()
這是延續上一個case,為了避免因為getter傳出個物件讓外部有機會修其物件,應該替他實施clone

PinNumber p=new PinNumber(new StringBuffer("Tom"),10000);
StringBuffer owe=p.getOwner();
owe.append(" cat");//不可變被破壞


修改

public StringBuffer getOwner{//getter
return owner.clone();
}

clone又分成shallow cloning(淺層clone 只copy reference,依然共享實體)跟deep cloning(深層clone ,連reference的實體也copy一份)

case 65:使用ingeritance或delegation來定義immutable object
這個case探討三個定義immutable class的方法

  1. immutable inteface(不可變界面)

  2. 公共界面或基礎類別(common interface or base class)

  3. immutable delegation class



immutable inteface


提供一個只能取值的界面

interface Circle{
public double radius();
}
class MyCircle implements Circle{
double radius;
private MyCircle();
MyCircle createCircle(){
return new MyCircle();
}
void setRadius(double r){radius=r;}
}


類似這樣的設計,把建構式藏起來,用一個factor取代

Circle c=MyCircle.createCircle();
c.setRadius(100);

像這樣的程式就會編譯不通過,因為Circle沒有提供設值函式,這樣一來user就不能修改c
但是他是有破綻的

Circle c=MyCircle.createCircle();
((MyCircle)c).setRadius(100);

諸如此類,透過轉型就能破解

common interface or base class


因此有了進階的common interface or base class出現補救
他有三個要素

  1. 一個interface或abstract的父類別,宣告可被子類別(derived class)共用的immutable函式

  2. 一個子類別提供mutable函式實作

  3. 另一個子類別提供immutable函式實作




interface Circle{
public double radius();
}
class IMCircle implements Circle{
double radius;
private IMCircle();
IMCircle createCircle(){
return new IMCircle();
}
}
class MCircle implements Circle{
double radius;
private MCircle();
MCircle createCircle(){
return new MCircle();
}
void setRadius(double r){}
}


immutable delegation class


利用委託的方式讓物建不可變


class Circle{
double radius;
public Circle();
void setRadius(double r){}
double getRadius(){};
}
class ImmutableCircle{
private Circle c;
ImmutableCircle(){
c=new Circle();
}
double getRadius(){
return c.getRadius();
}
}

以下提出總結





技術優點缺點
immutable inteface實作簡單,無效率代價有破綻 可破壞
common interface or base class沒破綻,將immutable和mutable清楚分開需實作更多class跟繼承體
immutable delegation class無破綻,當無法修改源碼時可用效率較低


case 66:實作clone()記得呼叫super.clone()
要支援clone的class必須呼叫java.lang.Object的clone()
呼叫super.clone()可以確保java.lang.Object的clone()會被呼叫
java.lang.Object的clone()會創建一個正確型別的新物件並執行shallow clone
即使要實作deep clone也該呼叫
考慮下面一段程式

class House implements Cloneable
{

@Override
protected Object clone() throws CloneNotSupportedException {
// TODO Auto-generated method stub
return new House();
}
}

class MyHouse extends House{

}
...
public static void main(String[] args) {
// TODO Auto-generated method stub
MyHouse t1=new MyHouse();
try {
MyHouse t2=(MyHouse)t1.clone();
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

他沒有呼叫Object的clone
會出現如下錯誤訊息
Exception in thread "main" java.lang.ClassCastException: House cannot be cast to MyHouse
因為此時clone回傳的是House物件而非MyHouse

但是我只需要將程式碼小小修改

class House implements Cloneable
{

@Override
protected Object clone() throws CloneNotSupportedException {
// TODO Auto-generated method stub
return super.clone();
}
}
class MyHouse extends House{

}
...
public static void main(String[] args) {
// TODO Auto-generated method stub
MyHouse t1=new MyHouse();
try {
MyHouse t2=(MyHouse)t1.clone();
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

就不會有這問題了

case 67:別只依賴finalize()清理non-memory(記憶體之外)的資源
簡單來說像是socket或File等東西都應該手動去清除他,JVM不會替我們回收

class MySocket{
Socket ss;
....
void close(){//如果class有物件是會暫用non-memory資源,就該提供清除的函式
ss.close();
}

}

....
MySocket ss=new MySocket();
...
ss.close();


case 68:在建構室內呼叫non-final函式的時候要小心
當使用函式去初始化資料的時候,要小心多型帶來的例外

class Base{
private int val;
Base(){
val=lookup();
}
int lookup(){return 5;}
int values(){return val;}

}
class Dev extends Base{
private int num=10;
int lookup(){return num;}

}
...
Dev d=new Dev();
System.out.println(d.values());
//印出結果:0

為什麼是0呢,原來Dev呼叫的是int lookup(){return num;}而非int lookup(){return 5;}
但此時Dev的instance都還沒初始化完成,num此時是0而不是10

------------------------------------

沒有留言: