JavaRush /จาวาบล็อก /Random-TH /ยาชื่อสามัญใน Java คืออะไร

ยาชื่อสามัญใน Java คืออะไร

เผยแพร่ในกลุ่ม
สวัสดี! วันนี้เราจะพูดถึงยาชื่อสามัญ ต้องบอกว่าคุณจะได้เรียนรู้สิ่งใหม่ ๆ มากมาย! ไม่เพียงเท่านี้ แต่ยังจะมีการบรรยายเรื่องยาชื่อสามัญในครั้งต่อไปด้วย ยาชื่อสามัญใน Java คืออะไร - 1 ดังนั้นหากหัวข้อนี้น่าสนใจสำหรับคุณ แสดงว่าคุณโชคดี วันนี้คุณจะได้เรียนรู้มากมายเกี่ยวกับคุณสมบัติของยาชื่อสามัญ ถ้าไม่ก็ใจเย็น ๆ และผ่อนคลาย! :) นี่เป็นหัวข้อที่สำคัญมากและคุณจำเป็นต้องรู้ มาเริ่มกันด้วยคำถามง่ายๆ: "อะไร" และ "ทำไม" ยาชื่อสามัญคืออะไร? ข้อมูลทั่วไปคือประเภทที่มีพารามิเตอร์ เมื่อสร้างข้อมูลทั่วไป คุณไม่เพียงแต่ระบุประเภทของข้อมูลเท่านั้น แต่ยังรวมถึงประเภทของข้อมูลที่ควรใช้ด้วย ฉันคิดว่าตัวอย่างที่ชัดเจนที่สุดอยู่ในใจคุณแล้ว - นี่คือ ArrayList! โดยปกติแล้วเราจะสร้างมันขึ้นมาในโปรแกรมดังนี้:
import java.util.ArrayList;
import java.util.List;

public class Main {

   public static void main(String[] args) {

       List<String> myList1 = new ArrayList<>();
       myList1.add("Test String 1");
       myList1.add("Test String 2");
   }
}
ดังที่คุณอาจเดาได้ ลักษณะเฉพาะของรายการก็คือ ไม่สามารถ "ยัด" ทุกอย่างลงไปได้: มันใช้งานได้กับวัตถุStringเท่านั้น ตอนนี้เรามาดูประวัติศาสตร์ของ Java สั้น ๆ แล้วลองตอบคำถาม: "ทำไม" ในการทำเช่นนี้ พวกเราเองจะเขียนคลาส ArrayList เวอร์ชันที่เรียบง่าย รายการของเราสามารถเพิ่มข้อมูลลงในอาร์เรย์ภายในและรับข้อมูลนี้ได้เท่านั้น:
public class MyListClass {

   private Object[] data;
   private int count;

   public MyListClass() {
       this.data = new Object[10];
       this.count = 0;
   }

   public void add(Object o) {
       this.data[count] = o;
       count++;
   }

   public Object[] getData() {
       return data;
   }
}
สมมติว่าเราต้องการให้รายการของเราเก็บเฉพาะIntegerตัวเลข เราไม่มียาชื่อสามัญ เราไม่สามารถระบุอินสแตนซ์ o ของการตรวจสอบIntegerในไฟล์add(). จากนั้นทั้งชั้นเรียนของเราจะเหมาะสำหรับเท่านั้นIntegerและเราจะต้องเขียนคลาสเดียวกันสำหรับประเภทข้อมูลทั้งหมดที่มีอยู่ในโลก! เราตัดสินใจที่จะพึ่งพาโปรแกรมเมอร์ของเราและเพียงแสดงความคิดเห็นในโค้ดเพื่อที่พวกเขาจะได้ไม่ต้องเพิ่มสิ่งที่ไม่จำเป็นลงไป:
//use it ONLY with Integer data type
public void add(Object o) {
   this.data[count] = o;
   count++;
}
โปรแกรมเมอร์คนหนึ่งพลาดความคิดเห็นนี้และพยายามใส่ตัวเลขผสมกับสตริงในรายการโดยไม่ตั้งใจ จากนั้นจึงคำนวณผลรวม:
public class Main {

   public static void main(String[] args) {

       MyListClass list = new MyListClass();
       list.add(100);
       list.add(200);
       list.add("Lolkek");
       list.add("Shalala");

       Integer sum1 = (Integer) list.getData()[0] + (Integer) list.getData()[1];
       System.out.println(sum1);

       Integer sum2 = (Integer) list.getData()[2] + (Integer) list.getData()[3];
       System.out.println(sum2);
   }
}
เอาต์พุตคอนโซล: 300 ข้อยกเว้นในเธรด "main" java.lang.ClassCastException: java.lang.String ไม่สามารถส่งไปที่ java.lang.Integer ที่ Main.main(Main.java:14) สิ่งที่แย่ที่สุดในสถานการณ์นี้คืออะไร ห่างไกลจากการไม่ตั้งใจของโปรแกรมเมอร์ สิ่งที่แย่ที่สุดคือรหัสผิดไปอยู่ในตำแหน่งสำคัญในโปรแกรมของเราและคอมไพล์ได้สำเร็จ ตอนนี้เราจะเห็นข้อผิดพลาดไม่ใช่ในขั้นตอนการเขียนโค้ด แต่เฉพาะในขั้นตอนการทดสอบเท่านั้น (และนี่คือกรณีที่ดีที่สุด!) การแก้ไขข้อบกพร่องในภายหลังในการพัฒนามีค่าใช้จ่ายมากขึ้นทั้งเงินและเวลา นี่เป็นข้อได้เปรียบของยาชื่อสามัญ: คลาสทั่วไปจะช่วยให้โปรแกรมเมอร์ที่โชคร้ายตรวจพบข้อผิดพลาดได้ทันที รหัสจะไม่รวบรวม!
import java.util.ArrayList;
import java.util.List;

public class Main {

   public static void main(String[] args) {

       List<Integer> myList1 = new ArrayList<>();

       myList1.add(100);
       myList1.add(100);
       myList1.add("Lolkek");//error!
       myList1.add("Shalala");//error!
   }
}
โปรแกรมเมอร์จะ "รู้สึกตัว" ทันทีและแก้ไขตัวเองทันที อย่างไรก็ตาม เราไม่จำเป็นต้องสร้างชั้นเรียนของเราเองListเพื่อดูข้อผิดพลาดประเภทนี้ เพียงลบวงเล็บประเภท ( <Integer>) ออกจาก ArrayList ปกติ!
import java.util.ArrayList;
import java.util.List;

public class Main {

   public static void main(String[] args) {

      List list = new ArrayList();

      list.add(100);
      list.add(200);
      list.add("Lolkek");
      list.add("Shalala");

       System.out.println((Integer) list.get(0) + (Integer) list.get(1));
       System.out.println((Integer) list.get(2) + (Integer) list.get(3));
   }
}
เอาต์พุตคอนโซล: 300 ข้อยกเว้นในเธรด "main" java.lang.ClassCastException: java.lang.String ไม่สามารถส่งไปที่ java.lang.Integer ที่ Main.main(Main.java:16) นั่นคือ แม้จะใช้เครื่องมือ "native" Java คุณสามารถทำผิดพลาดและสร้างคอลเลกชันที่ไม่ปลอดภัยได้ อย่างไรก็ตาม หากเราวางโค้ดนี้ลงใน IDEa เราจะเห็นคำเตือน: “ Unchecked call to add(E) as a member of raw type of java.util.List ” มันบอกเราว่ามีบางอย่างอาจผิดพลาดเมื่อเพิ่มองค์ประกอบลงใน คอลเลกชันที่ไม่มียาชื่อสามัญไม่ใช่วิธีนี้ แต่วลี "ประเภทดิบ" หมายถึงอะไร? การแปลตามตัวอักษรจะค่อนข้างแม่นยำ - " ประเภทดิบ " หรือ " ประเภทสกปรก " Raw typeเป็นคลาสทั่วไปที่ถูกลบประเภทออกไป กล่าวอีกนัยหนึ่งList myList1นี่คือRaw type. สิ่งที่ตรงกันข้ามraw typeคือgeneric typeคลาสทั่วไป (หรือที่เรียกว่าคลาสparameterized type) ที่สร้างขึ้นอย่างถูกต้องพร้อมข้อกำหนดประเภท ตัวอย่างเช่น, List<String> myList1. คุณอาจมีคำถาม: เหตุใดจึงได้รับอนุญาตให้ใช้raw types? เหตุผลง่ายๆ ผู้สร้าง Java ทิ้งการสนับสนุนในภาษาraw typesไว้เพื่อไม่ให้เกิดปัญหาความเข้ากันได้ เมื่อถึงเวลาที่ Java 5.0 เปิดตัว (ข้อมูลทั่วไปปรากฏขึ้นเป็นครั้งแรกในเวอร์ชันนี้) มีการเขียนโค้ดจำนวนมากโดยใช้raw types. ดังนั้นความเป็นไปได้นี้ยังคงมีอยู่จนถึงทุกวันนี้ เราได้กล่าวถึงหนังสือคลาสสิกของ Joshua Bloch เรื่อง “Effective Java” มากกว่าหนึ่งครั้งในการบรรยาย ในฐานะหนึ่งในผู้สร้างภาษา เขาไม่ได้ละเลยหัวข้อการใช้raw typesและ ใน generic typesหนังสือ ยาชื่อสามัญใน Java คืออะไร - 2บทที่ 23 ของหนังสือเล่มนี้มีชื่อที่ไพเราะมาก: “อย่าใช้ชนิดดิบในโค้ดใหม่” นี่คือสิ่งที่คุณต้องจำ เมื่อใช้คลาสทั่วไป ห้ามแปลงคลาสเหล่านั้นgeneric typeเป็นraw type.

วิธีการพิมพ์

Java อนุญาตให้คุณพิมพ์แต่ละวิธี โดยสร้างสิ่งที่เรียกว่าวิธีทั่วไป เหตุใดวิธีการดังกล่าวจึงสะดวก? ประการแรก เนื่องจากช่วยให้คุณสามารถทำงานกับพารามิเตอร์ประเภทต่างๆ ได้ หากสามารถนำตรรกะเดียวกันไปใช้กับประเภทต่างๆ ได้อย่างปลอดภัย วิธีการทั่วไปก็เป็นทางออกที่ดี ลองดูตัวอย่าง สมมติว่าเรามีรายการบางmyList1อย่าง เราต้องการลบค่าทั้งหมดออกจากนั้นและเติมช่องว่างทั้งหมดด้วยค่าใหม่ นี่คือลักษณะของชั้นเรียนของเราที่มีวิธีการทั่วไป:
public class TestClass {

   public static <T> void fill(List<T> list, T val) {
       for (int i = 0; i < list.size(); i++)
           list.set(i, val);
   }

   public static void main(String[] args) {

       List<String> strings = new ArrayList<>();
       strings.add("Старая строка 1");
       strings.add("Старая строка 2");
       strings.add("Старая строка 3");

       fill(strings, "Новая строка");

       System.out.println(strings);

       List<Integer> numbers = new ArrayList<>();
       numbers.add(1);
       numbers.add(2);
       numbers.add(3);

       fill(numbers, 888);
       System.out.println(numbers);
   }
}
ให้ความสนใจกับไวยากรณ์มันดูผิดปกติเล็กน้อย:
public static <T> void fill(List<T> list, T val)
ประเภทการส่งคืนนำหน้าด้วย <T> ซึ่งระบุวิธีการทั่วไป ในกรณีนี้ วิธีการใช้พารามิเตอร์ 2 ตัวเป็นอินพุต: รายการของอ็อบเจ็กต์ T และอีกอ็อบเจ็กต์แยก T โดยใช้ <T> จึงสามารถพิมพ์วิธีการได้: เราไม่สามารถส่งรายการสตริงและตัวเลขไปที่นั่นได้ รายการสตริงและสตริง รายการตัวเลขและตัวเลข รายการวัตถุของเราCatและวัตถุอื่นCat- นั่นคือวิธีเดียว วิธีการนี้main()แสดงให้เห็นอย่างชัดเจนว่าวิธีการนี้fill()ทำงานกับข้อมูลประเภทต่างๆ ได้อย่างง่ายดาย ขั้นแรก จะต้องป้อนรายการสตริงและสตริง จากนั้นป้อนรายการตัวเลขและตัวเลข เอาต์พุตคอนโซล: [Newline, Newline, Newline] [888, 888, 888] ลองนึกภาพว่าfill()เราต้องการตรรกะของวิธีการสำหรับคลาสที่แตกต่างกัน 30 คลาส และเราไม่มีวิธีการทั่วไป เราจะถูกบังคับให้เขียนวิธีเดียวกัน 30 ครั้งสำหรับประเภทข้อมูลที่ต่างกัน! แต่ด้วยวิธีการทั่วไป เราจึงสามารถใช้โค้ดของเราซ้ำได้! :)

ชั้นเรียนที่พิมพ์

คุณไม่เพียงแต่สามารถใช้คลาสทั่วไปที่มีให้ใน Java เท่านั้น แต่ยังสร้างคลาสของคุณเองได้ด้วย! นี่เป็นตัวอย่างง่ายๆ:
public class Box<T> {

   private T t;

   public void set(T t) {
       this.t = t;
   }

   public T get() {
       return t;
   }

   public static void main(String[] args) {

       Box<String> stringBox = new Box<>();

       stringBox.set("Старая строка");
       System.out.println(stringBox.get());
       stringBox.set("Новая строка");

       System.out.println(stringBox.get());

       stringBox.set(12345);//ошибка компиляции!
   }
}
Box<T>พิมพ์ ชั้นเรียนของเรา (“กล่อง”) หลังจากกำหนดประเภทข้อมูล ( ) ให้กับมันในระหว่างการสร้าง<T>เราจะไม่สามารถวางวัตถุประเภทอื่นลงไปได้อีกต่อไป นี้สามารถเห็นได้ในตัวอย่าง เมื่อสร้าง เราระบุว่าวัตถุของเราจะทำงานกับสตริง:
Box<String> stringBox = new Box<>();
และเมื่อในบรรทัดสุดท้ายของโค้ดที่เราพยายามใส่หมายเลข 12345 ลงในช่อง เราได้รับข้อผิดพลาดในการคอมไพล์! เพียงเท่านี้ เราก็สร้างคลาสทั่วไปของเราเองขึ้นมา! :) นี่เป็นการสรุปการบรรยายของเราในวันนี้ แต่เราไม่ได้บอกลายาสามัญ! ในการบรรยายครั้งต่อไป เราจะพูดถึงคุณสมบัติขั้นสูงเพิ่มเติม ดังนั้นอย่าเพิ่งบอกลา! ) ขอให้โชคดีในการศึกษาของคุณ! :)
ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION