Re: [問題] 多形問題
※ 引述《TWTRubiks (阿哲)》之銘言:
: 目的,我想要把name和id宣告成private然後印出來(宣告成public就很簡單)
: 不過我想要宣告成private印出來(試了很多方法),都無法解決,想請問有甚麼比較好的解
: 決方法
: 我試過了用類似Getid(),不過還是不行因為還要Setid(),這樣會讓Person變得很雜
: 目前知道的解決方法,使用protected可以解決!
: 但是想請問還有其他方法嗎?!
: package aaa;
: abstract class Person{
: private String name;
: private int id;
: abstract void printPerson();
: }
: class Student extends Person{
: private double g1,g2,g3;
: public Student(String name,int id,double g1,double g2,double g3){
:
: //這裡我就不知道該怎麼打了,因為我式宣告成private,也以也不能打 ex. thid.id = id
: this.g1=g1; this.g2=g2; this.g3=g3;
: }
: public void printPerson(){
: double average,sum;
: sum=g1+g2+g3;
: average=sum/3;
: // 當然這裡也悲劇
: // System.out.println("姓名:"+name);
: // System.out.println("編號:"+getID());
: System.out.println("總分:"+sum);
: System.out.println("平均:"+average);
: }
: }
: public class AAA {
: /**
: * @param args
: */
: public static void main(String[] args) {
: // TODO Auto-generated method stub
: Student S=new Student("TWTRubiks",1100104105,96,99,60);
: S.printPerson();
: }
: }
不是用了繼承就算多形。
單純以這個例子來說,它並不太好,即使用 protected 來獲得存取的能力。
先看原來的 Person:
abstract class Person{
private String name;
private int id;
abstract void printPerson();
}
你想要讓子類別能客制它 printPerson() 的功能,
但又不想要洩露內部分 id 與 name,那麼用參數傳入是一種方式,
索性先改成這樣吧:
abstract class Person{
private String name;
private int id;
abstract void printPerson(int id, String name);
}
把參數方入 printPerson 看似解決了困境,
但這有個問題到時要實作時,得有人能取得 Person 內的 id 與 name。
目前沒有這個擁有合理權限的人
(別以為我們在鬼打牆,問題確實推進了一小步)
回想一下,我們設計這個類別的期待是什麼:
『保持私有變數的情況下,容許繼承者客製化』
常見手法是引入一個『間接層』,它可以是另一個類別,或另一個方法
(這實在很難用簡單的話說明,『做』就對了!?)
將 Person 引入間接方法:
public abstract class Person {
private String name;
private int id;
public String toFormatString(int id, String name) {
return "id: " + id + ", name: " + name;
}
final public void printPerson() {
System.out.println(toFormatString(id, name));
}
}
改寫後,看起來還不賴。子類別只要呼叫 printPersion(...)
就能印出一些資訊,而 id 與 name 完全就是 printPersion(...) 內
自行去要到的,它絕對對於 private field 有讀寫的權利。
於是我們可以開始實作 Student 類別:
class Student extends Person {
private double g1, g2, g3;
public Student(String name, int id,
double g1, double g2, double g3) {
this.g1 = g1;
this.g2 = g2;
this.g3 = g3;
}
@Override
public String toFormatString(int id, String name) {
StringBuffer sb = new StringBuffer();
sb.append("姓名:").append(name).append("\n");
sb.append("編號:").append(id).append("\n");
double sum = g1 + g2 + g3;
sb.append("總分:").append(sum).append("\n");
sb.append("平均:").append(sum / 3).append("\n");
return sb.toString();
}
}
看起來情況還不賴,但我們似乎漏了什麼!?
name 與 id 要如何在沒有權限的情況下指定給 Person !?
如其它版友說的,setter 是一種方法。
但若一個實用的 Person 那就要有足夠的資訊來表達它,
以你先前的『定義』來說,是要有 id 與 name 才算是個合格的 Person
所以,這要視為它應該『出生』時就有的,
我們替它加上建構子:
public abstract class Person {
private String name;
private int id;
public Person(int id, String name) {
this.id = id;
this.name = name;
}
public String toFormatString(id int, String name) {
return "id: " + id + ", name: " + name;
}
final public void printPerson() {
System.out.println(toFormatString(id, name));
}
}
一旦有了建構子,你的子類別就不需要個別去存取 id 與 name
我們得這麼改寫:
public Student(String name, int id,
double g1, double g2, double g3) {
super(id, name);
this.g1 = g1;
this.g2 = g2;
this.g3 = g3;
}
這樣一來,你的 id 與 name 就透過了父類別的建構子指設定完成。
完工後寫個 main 執行一下:
public static void main(String[] args) {
Vector<Student> students = new Vector<Student>();
students.add(new Student("人蔘", 1, 60D, 60D, 60D));
students.add(new Student("當歸", 1, 60D, 98D, 62D));
for (Student s : students) {
s.printPerson();
}
}
=============================================================
姓名:人蔘
編號:1
總分:180.0
平均:60.0
姓名:當歸
編號:1
總分:220.0
平均:73.33333333333333
=============================================================
好吧!從頭到現在我們做了些什麼?
多加了一個 method,多寫了一個建構子?
一些入門書說到多形時,就單純寫個繼承把原來的 method 蓋掉而已。
但那只是一種語法上的體驗,但真正實用的是語意上的體驗。
這個過程,我們其實用了 Template Method。
原先你打算要作為『延伸點』的 printPerson() 方法,
反而用了 final 明確表示了它不可以被子類別修改:
final public void printPerson() {
System.out.println(toFormatString(id, name));
}
這確保它在繼承數的基本行為不變:取得要印出的結果後顯示出來。
我們容取客製化的是要印出的內容 toFormatString:
public String toFormatString(id int, String name) {
return "id: " + id + ", name: " + name;
}
toFormatString 你也可以讓它是抽象的,
這只是表示『你不打算提供預設的實作』。
若它為抽象的,意義上就是『子類別必需實作它』(非 optional)
我們提供了 Person 這類別在『顯示內容』的方法,
若有需要客製化內容則在子類別實作自己的 toFormatString。
無論如何,你都能呼叫 printPerson() 來『顯示內容』
這樣的設計有彈性又不曝露,像可以隨手新增不同的應用:
public class 華府員工 extends Person {
public 華府員工(int id) {
super(id, "從今天起,你沒有名字");
}
@Override
public String toFormatString(int id, String name) {
return name + ",你就叫 " + id;
}
public static void main(String[] args) {
華府員工 m = new 華府員工(9527);
m.printPerson();
}
}
--
※ 發信站: 批踢踢實業坊(ptt.cc)
◆ From: 114.25.210.5
※ 編輯: qrtt1 來自: 114.25.210.5 (10/29 00:02)
推
10/29 01:01, , 1F
10/29 01:01, 1F
→
10/29 09:23, , 2F
10/29 09:23, 2F
推
10/29 13:15, , 3F
10/29 13:15, 3F
推
10/29 16:38, , 4F
10/29 16:38, 4F
→
10/29 20:46, , 5F
10/29 20:46, 5F
推
10/29 20:56, , 6F
10/29 20:56, 6F
→
10/29 20:57, , 7F
10/29 20:57, 7F
→
10/29 20:58, , 8F
10/29 20:58, 8F
※ 編輯: qrtt1 來自: 114.25.245.200 (10/29 20:59)
→
10/29 20:59, , 9F
10/29 20:59, 9F
推
10/31 10:11, , 10F
10/31 10:11, 10F
討論串 (同標題文章)