作者qrtt1 (有些事,有时候。。。)
看板java
标题Re: [问题] 多形问题
时间Mon Oct 29 00:01:13 2012
※ 引述《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)
1F:推 No:toFormatString可以用protected 10/29 01:01
2F:→ qrtt1:嗯。改成 protected 确实比较好 :D 10/29 09:23
3F:推 polppk:推这篇! 小bug 无伤大雅 ,第二个 Person Class的参数错了 10/29 13:15
4F:推 TWTRubiks:很多东西没看过Q_Q 10/29 16:38
5F:→ qrtt1:唔,哪有错@@? 10/29 20:46
6F:推 polppk:我看错了 @@ sorry ....哈哈 10/29 20:56
7F:→ polppk:toFormatString(id int,String name) 10/29 20:57
8F:→ qrtt1:找不到的 bug 最难修,还好真的是没有xd 10/29 20:58
※ 编辑: qrtt1 来自: 114.25.245.200 (10/29 20:59)
9F:→ qrtt1:感谢,修好了. :D 10/29 20:59
10F:推 gmoz:就甘心 10/31 10:11