1. สืบทอดทุกคลาสObject

คลาสทั้งหมดใน Java สืบทอดObjectคลาส โดยปริยาย

เราจะวิเคราะห์ว่ามรดกคืออะไรและทำงานอย่างไรใน Java ในภารกิจ Java Core สำหรับตอนนี้ เราจะพิจารณาข้อเท็จจริงง่ายๆ ประการหนึ่งต่อไปนี้:

สามารถกำหนดวัตถุของคลาสใดก็ได้ให้กับObjectตัวแปร ตัวอย่าง:

รหัส บันทึก
Object o = new Scanner(System.in);
ตัวแปรoเก็บการอ้างอิงถึงScannerวัตถุ
Object o = new String();
ตัวแปรoเก็บการอ้างอิงถึงStringวัตถุ
Object o = new Integer(15);
ตัวแปรoเก็บการอ้างอิงถึงIntegerวัตถุ
Object o = "Hello";
ตัวแปรoเก็บการอ้างอิงถึงStringวัตถุ

นี่คือจุดสิ้นสุดของข่าวดี คอมไพลเลอร์ไม่ติดตามประเภทดั้งเดิมของวัตถุที่บันทึกไว้ในObjectตัวแปร ดังนั้นคุณจึงไม่สามารถ เรียกใช้เมธอดบนวัตถุที่บันทึกไว้นอกเหนือจากเมธอดของObjectคลาส

หากคุณต้องการเรียกใช้เมธอดที่เกี่ยวข้องกับประเภทดั้งเดิมของออบเจ็กต์ คุณต้องบันทึกการอ้างอิงถึงเมธอดในตัวแปรประเภทที่ถูกต้องก่อน จากนั้นจึงเรียกใช้เมธอดในตัวแปรนั้น:

รหัส บันทึก
Object o = new Scanner(System.in);
int x = o.nextInt();
โปรแกรมจะไม่คอมไพล์ ชั้นObjectเรียนไม่มีnextInt()วิธีการ
Object o = new Scanner(System.in);

Scanner console = (Scanner) o;

int x = console.nextInt();
สิ่งนี้จะทำงาน

ที่นี่เราบันทึกการอ้างอิงถึงวัตถุ ScannerในScannerตัวแปรโดยใช้ตัวดำเนินการ typecast

คุณไม่สามารถไปกำหนดObjectตัวแปรให้กับตัวแปร Scanner ได้ แม้ว่าObjectตัวแปรจะเก็บการอ้างอิงScannerวัตถุ ก็ตาม แต่คุณสามารถทำได้หากคุณใช้ตัวดำเนินการ typecastซึ่งคุณรู้อยู่แล้ว นี่คือลักษณะทั่วไป:

Type name1 = (Type) name2;

name1ชื่อของตัวแปรอยู่ที่ไหนTypeและname2เป็นชื่อของObjectตัวแปรที่เก็บการอ้างอิงถึงTypeวัตถุ

การพิมพ์

หากประเภทของตัวแปรและประเภทของวัตถุไม่ตรงกัน จากนั้น a ClassCastExceptionจะถูกโยนทิ้ง ตัวอย่าง:

รหัส บันทึก
Object o = new Integer(5);
String s = (String) o;
ข้อผิดพลาดจะเกิดขึ้นที่รันไทม์:
a ClassCastExceptionจะถูกส่งมาที่นี่

มีวิธีหลีกเลี่ยงข้อผิดพลาดนี้ใน Java: เราทำได้โดยตรวจสอบประเภทของวัตถุที่จัดเก็บไว้ในตัวแปร :

name instanceof Type

ตัวinstanceofดำเนินการตรวจสอบว่าnameตัวแปรเป็นTypeวัตถุหรือไม่

ตัวอย่างเช่น ลองค้นหาสตริงในอาร์เรย์ของออบเจกต์ที่หลากหลาย:

รหัส บันทึก
Object[] objects = {10, "Hello", 3.14};

for (int i = 0; i < objects.length; i++)
{
   if (objects[i] instanceof String)
   {
      String s = (String) objects[i];
      System.out.println(s);
   }
}
การทำกล่องอัตโนมัติจะแปลงค่าเหล่านี้เป็นInteger, String, และDoubleตามลำดับ

วนซ้ำอาร์เรย์ของอ็อบเจกต์

ถ้าอString

อบเจกต์นั้นเป็นแบบบันทึกไปยังStringตัวแปร
แสดงตัวแปรบนหน้าจอ


2. เหตุใดยาชื่อสามัญจึงปรากฏขึ้น - คอลเลกชัน

กลับไปที่คอลเลกชันกันเถอะ

ทันทีที่ผู้พัฒนา Java สร้างArrayListคลาส พวกเขาต้องการทำให้เป็นคลาสสากล เพื่อให้สามารถเก็บออบเจกต์ประเภทใดก็ได้ ดังนั้นพวกเขาจึงใช้อาร์เรย์ของObjects เพื่อเก็บองค์ประกอบต่างๆ

จุดแข็งของวิธีนี้คือคุณสามารถเพิ่มวัตถุประเภทใดก็ได้ในคอลเลกชัน

แน่นอนว่ามีจุดอ่อนหลายประการ

ข้อเสีย1.

จำเป็นต้องเขียนตัวดำเนินการแปลงประเภทเสมอเมื่อดึงองค์ประกอบจากคอลเล็กชัน:

รหัส บันทึก
ArrayList numbers = new ArrayList();


for (int i = 0; i < 10; i++)
   numbers.add(i * 10);


int sum = 0;
for (int i = 0; i < 10; i++)
{
   sum = sum + (Integer) numbers.get(i);
}
สร้างคอลเลกชันเพื่อจัดเก็บการอ้างอิงถึงObjectวัตถุ

เติมคอลเลกชันด้วยตัวเลข10, 20, ... 100;



รวมองค์ประกอบของคอลเลก


ชัน Typecasting เป็นสิ่งจำเป็น

ข้อเสีย 2.

ไม่มีการรับประกันว่าคอลเลกชันมีองค์ประกอบเฉพาะประเภท

รหัส บันทึก
ArrayList numbers = new ArrayList();


for (int i = 0; i < 10; i++)
   numbers.add(i * 2.5);


int sum = 0;
for (int i = 0; i < 10; i++)
{
   sum = sum + (Integer) numbers.get(i);
}
สร้างคอลเลกชันเพื่อจัดเก็บการอ้างอิงถึงObjectวัตถุ

เราเติมคอลเลกชันด้วยตัวเลขที่แสดงเป็นDoubleวัตถุ:
0.0, 2.5, 5.0, ...


รวมองค์ประกอบของคอล เลก


ชัน จะมีข้อผิดพลาด: a Doubleไม่สามารถส่งไปยังInteger

สามารถใส่ข้อมูลลงในคอลเลกชันได้ทุกที่:

  • ด้วยวิธีอื่น
  • ในโปรแกรมอื่น
  • จากไฟล์
  • ผ่านเครือข่าย

ข้อเสีย3.

ข้อมูลในคอลเลกชันสามารถเปลี่ยนแปลงได้โดยไม่ตั้งใจ

คุณสามารถส่งคอลเลกชันที่เต็มไปด้วยข้อมูลของคุณไปยังวิธีการบางอย่าง วิธีการนั้นเขียนโดยโปรแกรมเมอร์อื่น เพิ่มข้อมูลลงในคอลเลกชันของคุณ

ชื่อของคอลเล็กชันไม่ได้ระบุอย่างชัดเจนว่าสามารถเก็บข้อมูลประเภทใดได้บ้าง และแม้ว่าคุณจะตั้งชื่อตัวแปรของคุณอย่างชัดเจน แต่การอ้างอิงถึงตัวแปรนั้นสามารถส่งต่อไปยังเมธอดต่างๆ ได้นับสิบเมธอด และเมธอดเหล่านั้นจะไม่รู้อะไรเลยเกี่ยวกับชื่อดั้งเดิมของตัวแปร


3. ยาสามัญ

Generics ใน Java

ใน Java ปัญหาเหล่านี้จะหมดไปโดยสิ่งเจ๋งๆ ที่เรียกว่า generics

ใน Java ข้อมูลทั่วไปหมายถึงความสามารถในการเพิ่มพารามิเตอร์ประเภทให้กับประเภท ผลลัพธ์คือประเภทคอมโพสิตที่ซับซ้อน มุมมองทั่วไปของประเภทคอมโพสิตคือ:

ClassName<TypeParameter>

นี่คือคลาสทั่วไป และสามารถใช้ได้ทุกที่ที่คุณใช้ชั้นเรียนตามปกติ

รหัส คำอธิบาย
ArrayList<Integer> list;
การสร้างตัวแปร
list = new ArrayList<Integer> ();
การสร้างวัตถุ
ArrayList<Integer>[] array;
การสร้างอาร์เรย์

เฉพาะIntegerตัวแปรเท่านั้นที่สามารถจัดเก็บในคอลเลกชันดังกล่าวได้:

รหัส คำอธิบาย
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(new Integer(1));
list.add(2);
list.add("Hello");
ArrayListคอลเลกชันที่มีIntegerองค์ประกอบ
ได้รับอนุญาต
และจะใช้งานได้เช่นกัน
กล่องอัตโนมัติ

แต่สิ่งนี้ไม่ได้รับอนุญาต: ข้อผิดพลาดในการรวบรวม

คุณจะได้เรียนรู้วิธีสร้างคลาสของคุณเองด้วยพารามิเตอร์ประเภทในภารกิจ Java Collections สำหรับตอนนี้ เราจะดูวิธีการใช้งานและวิธีการทำงาน


4. ยาชื่อสามัญทำงานอย่างไร

ที่จริงแล้วยาชื่อสามัญนั้นมีความดั้งเดิมอย่างมาก

คอมไพเลอร์แทนที่ประเภททั่วไปด้วยประเภทสามัญ แต่เมื่อเมธอดของประเภททั่วไปถูกใช้ คอมไพเลอร์จะเพิ่มตัวดำเนินการ typecast เพื่อส่งพารามิเตอร์ไปยังพารามิเตอร์ประเภท:

รหัส สิ่งที่คอมไพเลอร์ทำ
ArrayList<Integer> list = new ArrayList<Integer>();
ArrayList list = new ArrayList();
list.add(1);
list.add( (Integer) 1 );
int x = list.get(0);
int x = (Integer) list.get(0);
list.set(0, 10);
list.set(0, (Integer) 10);

สมมติว่าเรามีวิธีที่รวมตัวเลขในชุดจำนวนเต็ม:

รหัส สิ่งที่คอมไพเลอร์ทำ
public int sum(ArrayList<Integer> numbers)
{
   int result = 0;

   for (int i = 0; i < numbers.size(); i++)
      result = result + numbers.get(i);

   return result;
}
public int sum(ArrayList numbers)
{
   int result = 0;

   for (int i = 0; i < numbers.size(); i++)
      result = result + (Integer) numbers.get(i);

   return result;
}

กล่าวอีกนัยหนึ่ง ยาชื่อสามัญเป็นน้ำตาลวากยสัมพันธ์ชนิดหนึ่ง เหมือนกับการบอกอัตโนมัติ แต่เพิ่มเติมอีกเล็กน้อย ด้วย autoboxing คอมไพเลอร์จะเพิ่มเมธอดสำหรับการแปลง an intเป็น an Integerและในทางกลับกัน และสำหรับข้อมูลทั่วไปจะเพิ่มตัวดำเนินการ typecast

หลังจากที่คอมไพเลอร์คอมไพล์คลาสทั่วไปของคุณด้วยพารามิเตอร์ประเภทแล้ว คลาสเหล่านั้นจะถูกแปลงเป็นคลาสธรรมดาและตัวดำเนินการ typecast ข้อมูลเกี่ยวกับอาร์กิวเมนต์ประเภทที่ส่งผ่านไปยังตัวแปรประเภททั่วไปจะหายไป เอฟเฟกต์นี้เรียกอีกอย่างว่าการลบแบบ

บางครั้งโปรแกรมเมอร์ที่เขียนคลาสทั่วไป (คลาสที่มีพารามิเตอร์ประเภท) ต้องการข้อมูลเกี่ยวกับประเภทที่ส่งผ่านเป็นอาร์กิวเมนต์จริงๆ ในภารกิจ Java Collections คุณจะได้เรียนรู้วิธีจัดการกับสิ่งนี้และสิ่งที่เกี่ยวข้อง



5. ข้อเท็จจริงบางประการเกี่ยวกับยาชื่อสามัญ

ต่อไปนี้เป็นข้อเท็จจริงที่น่าสนใจเกี่ยวกับยาชื่อสามัญ

คลาสสามารถมีพารามิเตอร์หลายประเภท ดูเหมือนว่า:

ClassName<TypeParameter1, TypeParameter2, TypeParameter3>

จริงๆ แล้วนี่ไม่น่าแปลกใจเลย ในทุกที่ที่คอมไพเลอร์สามารถเพิ่มตัวดำเนินการเพื่อแคสต์เป็นประเภทเดียว ก็จะสามารถเพิ่มตัวดำเนินการประเภทแคสต์ได้หลายตัว

ตัวอย่าง:

รหัส บันทึก
HashMap<Integer, String> map = new HashMap<Integer, String>();
map.put(7, "Hello");
map.put(-15, "Hello");
putพารามิเตอร์ตัวแรกของเมธอดคือ an และIntegerตัวที่สองคือ aString

นอกจากนี้ยังสามารถใช้ประเภททั่วไปเป็นพารามิเตอร์ได้ อีกด้วย ดูเหมือนว่า:

ClassName<TypeParameter<TypeParameterParameter>>

สมมติว่าเราต้องการสร้างรายการที่จะจัดเก็บรายการสตริง ในกรณีนี้เราจะได้สิ่งนี้:

// List of greetings
ArrayList<String> listHello = new ArrayList<String>();
listHello.add ("Hello");
listHello.add ("Hi");

// List of goodbyes
ArrayList<String> listBye = new ArrayList<String>();
listBye.add("Bye");
listBye.add ("Goodbye");

// List of lists
ArrayList<ArrayList<String>> lists = new ArrayList<ArrayList<String>>();
lists.add(listHello);
lists.add(listBye);

ประเภททั่วไป (ประเภทที่มีพารามิเตอร์ประเภท) สามารถใช้เป็นประเภทอาร์เรย์ได้ ดูเหมือนว่า:

ClassName<TypeParameter>[] array = new ClassName<TypeParameter>[size];

ไม่มีอะไรมหัศจรรย์เกิดขึ้นที่นี่: วงเล็บมุมระบุชื่อประเภท:

รหัส คู่ที่ไม่ใช่ทั่วไป
ArrayList<String>[] list = new ArrayList<String>[10];
StringArrayList[] list = new StringArrayList[10];
ArrayList<Integer>[] list = new ArrayList<Integer>[10];
IntegerArrayList[] list = new IntegerArrayList[10];
ArrayList<Scanner>[] list = new ArrayList<Scanner>[10];
ScannerArrayList[] list = new ScannerArrayList[10];