Object และ Generics
class ทุกตัว ใน java จะถูก inherit มาจาก java.lang.Object ซึ่ง Compiler จะเป็นตัว
จัดการจาก class A {} เป็น class A extends Object {} ซึ่งทำให้ได้รับ Method ต่างๆมาด้วยเช่น
เรามักนิยม Override สอง Method ที่สำคัญคือ toString(); เพื่อแสดงค่าของ instance
ตอน print และ equals(); เพื่อกำหนดเงื่อนไขการเท่ากันของ instance เช่น
class A{}
System.out.println(new A());//ผลลัพท์ที่ได้จะเป็นค่า reference ของ instance A
ทีนี้ลอง Override method toString(); ดู
class A{ public String toString() {return "This is A";}
System.out.println(new A());//ผลลัพท์ที่ได้คือ This is A
จากตัวอย่างเป็นการใช้ข้อดีของ OOP นั่นก็คือ Polymophism นั่นเอง ทำให้ได้ผลลัพท์
ที่หลากหลายตามแต่ละ instance
ปกติแล้วเราจะไม่สามารถใช้ operator == เพื่อเปรียบเทียบค่าของ instance ได้
เรามักจะใช้ method equals(Object); แทนเช่น
class A {
int i;
public boolean equals(Object o) {
return i==((A)object).i;
}
}
System.out.println(new A());//ค่า reference จะขึ้นตัวชื่อ class ตามด้วย @xxx
System.out.println(new A()==new A());//false เพราะเป็นการเปรียบเทียบ reference
System.out.println(new A().equals(new A()));//true
Generics
Generics นั้นเพิ่งเพิ่มเข้ามาใน java 5 เพื่อประโยชน์ในการทำ type safe ซึ่งจะถูก
ตรวจสอบตั้งแต่ Compile time ลองดูตัวอย่าง code ที่ไม่ใช้ Generics ครับ
class Box {
Object component;
Box(Object component) {setComponent(component);}
void setComponent(Object component) {
this.component = component;
}
Object getComponent() {
return component;
}
}
Box b = new Box("text");
b.setComponent(new java.util.Date());
//..//
(String)b.getComponent();// Object นี้เป็น type อะไร
จากตัวอย่างมีโอกาสเกิด Error ตอน runtime ได้เนื่องจาก type ของ Component
ได้เปลี่ยนไปเป็น java.util.Date แล้ว ภาษาโปรแกรมที่ปลอดภัยควรตรวจสอบ type
ได้ตั้งแต่ตอน compile time ทีนี้ลองดูตัวอย่างการใช้ generic เพื่อช่วยเรือง
type safe ดังนี้
class Box<T> {
T component;
Box(T component) {setComponent(component);}
void setComponent(T component) {
this.component = component;
}
T getComponent() {
return component;
}
}
Box<String> boxString = new Box<String>("Hello");
String s = boxString.getComponent();//ไม่ต้อง casting เพราะ type เป็น String แน่นอน
boxString.setComponent(new java.util.Date());//Compile Error
int[]a = {0};
Box<int[]> boxInt = new Box<int[]>(a);
int[] i = boxInt.getComponent();//return type เป็น Array of integer
นอกจากจะทำ Generic ในระดับ Class, Constructor และ method ยังทำในระดับ
interface ได้ด้วยและสามารถมีได้หลาย type เช่น
interface Service<T,S> {
S register(T t);
}
เราสามารถ extends generic เพื่อที่จะเจาะจงว่าสามารถใช้ Type อะไรได้บ้างดังนี้
class A{}
class B extends A{}
class C extends A{}
class Box<T extends A> {
T component;
Box(T component) {setComponent(component);}
void setComponent(T component) {
this.component = component;
}
T getComponent() {
return component;
}
}
Box<A> a;
Box<B> b;
Box<C> c;
Box<String> s;//Compile Error!
Wildcards
จากตัวอย่างข้างต้นแม้ว่า B จะถูก inherit มาจาก A แต่เราไม่สามารถทำแบบนี้ได้ เช่น
Box<A> a = new Box<B>(new B());//Compile Error!
เนื่องจาก Compiler จะมอง Box<A> กับ Box<B> เป็นคนละ Type กัน แต่เราสามารถ
แก้ปัญหาได้ดังนี้
Box b = new Box<B>(new B());
Box c = new Box<C>(new C());
c = b;//Correct!
แต่กลับเป็นการเพิ่มปัญหาใหม่คือ Compiler จะยอมให้ c สามารถ set instance ของ
Type A ได้ และยอมให้ getComponent ออกมาเป็น type C ได้อีกด้วยดังนี้
c.setComponent(new B());
C c2 = (C)c.getComponent();//Runtime Error!
ภาษา Java จึงมี wildcards ? ซึ่งเป็นการระบุขอบเขตของ Type ดังตัวอย่างนี้
Box<?> b = new Box<B>(new B());
b.setComponent(new B());//Compile Error!
เนื่องจาก b สามารถถือ reference ของ Box<?> โดยที่ ? เป็น type A ใดๆก็ได้ เช่น
Box<?> b= new Box<A>(new A());
b = new Box<B>(new B());
b = new Box<C>(new C());
ความหมายของ ? จริงๆก็คือ unknown type ทำให้เราไม่สามารถเรียก setComponent
ได้เพราะ Compiler ไม่มีทางรู้ว่าตอน Runtime นั้น component จะเป็น Type อะไร
ในทางกลับกันที่เราสามารถ get ค่าของมันได้ โดยที่จะ return ออกมาเป็น Type A
(ตามที่ประกาศ โดย class Box<T extends A> {..} จากตัวอย่างข้างบน) เช่น
Box<?> b = new Box<B>(new B());
b.setComponent(new B());//Compile Error!
B b2 = (B)b.getComponent();//Correct!
นอกจากนี้เรายังสามารถระบุขอบเขตของ ? โดยใช้ extends ได้เช่น
Box<? extends A> b = new Box<A>(new A());
b = new Box<B>(new B());
จากตัวอย่างจะเ้ป็นการระับุขอบเขตบนนั่นคือ reference b จะรับได้แค่
Box<A> และ Box<B> ได้เท่านั้นและไม่สามารถเรียก setComponent แต่เรียกใช้
getComponent ซึ่ง return เป็น Object ได้
ในทางตรงกันข้ามหากเราใช้ ? ร่วมกับ super จะเป็นการระบุขอบเขตล่างของ ? เช่น
Box<? super B> b = new Box<A>(new A());
b = new Box(B)(new B());
เนื่องจาก Box<A> และั Box<B> ตรงตามเงื่อนไข คือเป็น super ของ
B<? super B> และเราสามารถเรียกใช้ b.setComponent(new B()); ได้เพราะเป็น
ลำดับล่างสุด ซึ่ง reference ใดๆสามารถถือได้ตามเงื่อนไขนี้ แต่เราไม่สามารถ
getComponent ได้ เพราะ Compiler ไม่มีทางรู้ว่าถือ reference ของ Type ลำดับ
อะไร
b.setComponent(new B());//Correct!
b.getComponent();Compile Error!
ไม่มีความเห็น