แนวคิดของการลบออกใน generics ใน Java คืออะไร?


คำตอบ:


200

โดยพื้นฐานแล้ววิธีการใช้งานทั่วไปใน Java ผ่านทางเครื่องมือรวบรวมคอมไพเลอร์ รหัสทั่วไปที่คอมไพล์แล้วจริง ๆ แล้วจะใช้java.lang.Objectทุกที่ที่คุณพูดถึงT(หรือพารามิเตอร์ชนิดอื่น) - และมีเมตาดาต้าที่จะบอกคอมไพเลอร์ว่ามันเป็นประเภททั่วไป

เมื่อคุณคอมไพล์โค้ดกับประเภทหรือเมธอดทั่วไปคอมไพเลอร์จะทำงานตามที่คุณต้องการ (เช่นอาร์กิวเมนต์ของประเภทTคืออะไร) และตรวจสอบในเวลาที่คอมไพล์ว่าคุณกำลังทำสิ่งที่ถูกต้อง แต่โค้ดที่ปล่อยออกมาพูดอีกครั้ง ในแง่ของjava.lang.Object- คอมไพเลอร์สร้างปลดเปลื้องพิเศษในกรณีที่จำเป็น ณ เวลาดำเนินการ a List<String> และ a List<Date>จะเหมือนกันทุกประการ คอมไพเลอร์ชนิดข้อมูลพิเศษถูกลบ

เปรียบเทียบสิ่งนี้กับพูด C # ที่ข้อมูลจะถูกเก็บไว้ที่เวลาดำเนินการให้รหัสที่มีการแสดงออกเช่นtypeof(T)ซึ่งเทียบเท่าT.class- ยกเว้นว่าหลังไม่ถูกต้อง (มีความแตกต่างเพิ่มเติมระหว่าง. NET generics และ Java generics โปรดคำนึงถึงคุณ) การลบประเภทเป็นแหล่งที่มาของข้อความเตือน / ข้อผิดพลาด "คี่" หลายข้อเมื่อจัดการกับ Java generics

แหล่งข้อมูลอื่น ๆ :


6
@Rogerio: ไม่วัตถุจะไม่มีประเภททั่วไปที่แตกต่างกัน เขตทราบชนิด แต่วัตถุที่ทำไม่ได้
Jon Skeet

8
@Rogerio: แน่นอน - เป็นเรื่องง่ายมากที่จะหาเวลาดำเนินการไม่ว่าสิ่งที่มีให้เฉพาะเป็นObject(ในสถานการณ์ที่พิมพ์ที่อ่อนแอ) เป็นจริงList<String>) ตัวอย่างเช่น ใน Java นั้นเป็นไปไม่ได้คุณจะพบว่ามันเป็นArrayListแต่ไม่ใช่ประเภทสามัญดั้งเดิม การเรียงลำดับของสิ่งนี้สามารถเกิดขึ้นในสถานการณ์ซีเรียลไลซ์เซชั่น / การดีซีเรียลไลเซชัน อีกตัวอย่างหนึ่งคือที่ซึ่งคอนเทนเนอร์จะต้องสามารถสร้างอินสแตนซ์ของประเภททั่วไปได้ - คุณต้องผ่านประเภทนั้นใน Java (as Class<T>)
Jon Skeet

6
ฉันไม่เคยอ้างว่ามันเป็นเสมอหรือเกือบเสมอปัญหา - แต่อย่างน้อยพอสมควรบ่อยปัญหาในประสบการณ์ของผม มีสถานที่ต่าง ๆ ที่ฉันถูกบังคับให้เพิ่มClass<T>พารามิเตอร์ให้กับตัวสร้าง (หรือวิธีการทั่วไป) เพียงเพราะ Java ไม่เก็บข้อมูลนั้น ดูEnumSet.allOfตัวอย่าง - อาร์กิวเมนต์ชนิดทั่วไปของเมธอดควรเพียงพอ เหตุใดฉันต้องระบุอาร์กิวเมนต์ "ปกติ" ด้วย คำตอบ: ลบประเภท สิ่งเหล่านี้ก่อให้เกิดมลพิษกับ API จากสิ่งที่คุณสนใจคุณใช้. NET generics มาก? (ต่อ)
Jon Skeet

5
ก่อนที่ฉันจะใช้. NET generics ฉันพบ Java generics ที่น่าอึดอัดใจในหลายวิธี (และ wildcarding ยังคงปวดหัวแม้ว่ารูปแบบความแปรปรวน "ผู้โทรระบุ" มีข้อได้เปรียบอย่างแน่นอน) - แต่หลังจากฉันใช้. NET generics ชั่วขณะหนึ่งที่ฉันเห็นว่ามีรูปแบบมากมายที่น่าอึดอัดใจหรือเป็นไปไม่ได้กับ Java generics มันเป็นความขัดแย้งของ Blub อีกครั้ง ฉันไม่ได้บอกว่า. NET generics ไม่มีข้อเสียเช่นกัน btw - มีความสัมพันธ์ประเภทต่าง ๆ ที่ไม่สามารถแสดงออกมาได้โชคไม่ดี - แต่ฉันชอบ Java generics มาก
Jon Skeet

5
@Rogerio: มีหลายสิ่งที่คุณสามารถทำได้ด้วยการไตร่ตรอง - แต่ฉันไม่คิดว่าฉันต้องการทำสิ่งเหล่านั้นบ่อยเท่าที่ฉันไม่สามารถทำได้กับ Java generics ฉันไม่ต้องการค้นหาอาร์กิวเมนต์ชนิดสำหรับเขตข้อมูลเกือบบ่อยเท่าที่ฉันต้องการค้นหาอาร์กิวเมนต์ชนิดของวัตถุจริง
Jon Skeet

41

มันเป็นแบบฝึกหัดที่น่าสนใจที่จะเห็นว่าคอมไพเลอร์กำลังทำอะไรเมื่อทำการลบ - ทำให้แนวคิดทั้งหมดเข้าใจง่ายขึ้นเล็กน้อย มีแฟล็กพิเศษที่คุณสามารถส่งต่อคอมไพเลอร์ไปยังไฟล์ java เอาต์พุตที่มีการลบข้อมูลทั่วไปและใส่ casts ตัวอย่าง:

javac -XD-printflat -d output_dir SomeFile.java

-printflatเป็นธงที่ได้รับการส่งออกไปยังคอมไพเลอร์ที่สร้างไฟล์ ( -XDส่วนหนึ่งคือสิ่งที่บอกjavacให้ส่งไปยัง jar ที่ปฏิบัติการได้จริง ๆ แล้วทำการคอมไพล์มากกว่าแค่javacแต่ฉันเชือนแช ... ) -d output_dirสิ่งที่จำเป็นเพราะคอมไพเลอร์ต้องการที่ที่จะวางไฟล์. java ใหม่

แน่นอนว่านี่เป็นมากกว่าการลบทิ้ง ทุกสิ่งอัตโนมัติที่คอมไพเลอร์ทำได้ที่นี่ ตัวอย่างเช่นตัวสร้างเริ่มต้นจะถูกแทรกเข้าไปด้วยforลูปforeach-style ใหม่จะถูกขยายเป็นforลูปปกติฯลฯ มันเป็นเรื่องดีที่ได้เห็นสิ่งเล็ก ๆ น้อย ๆ ที่เกิดขึ้นโดยอัตโนมัติ


29

การลบอย่างแท้จริงหมายถึงข้อมูลประเภทที่มีอยู่ในซอร์สโค้ดจะถูกลบออกจากไบต์ที่รวบรวม ให้เราเข้าใจเรื่องนี้ด้วยรหัส

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class GenericsErasure {
    public static void main(String args[]) {
        List<String> list = new ArrayList<String>();
        list.add("Hello");
        Iterator<String> iter = list.iterator();
        while(iter.hasNext()) {
            String s = iter.next();
            System.out.println(s);
        }
    }
}

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

import java.io.PrintStream;
import java.util.*;

public class GenericsErasure
{

    public GenericsErasure()
    {
    }

    public static void main(String args[])
    {
        List list = new ArrayList();
        list.add("Hello");
        String s;
        for(Iterator iter = list.iterator(); iter.hasNext(); System.out.println(s))
            s = (String)iter.next();

    }
} 

ฉันพยายามใช้ java decompiler เพื่อดูรหัสหลังจากการลบประเภทจากไฟล์. class, แต่ไฟล์. class ยังคงมีข้อมูลประเภท ฉันพยายามjigawotพูดว่ามันใช้งานได้
จริงใจ

25

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

เริ่มแรกนำเสนอที่ EclipseCon 2007 (ไม่มีให้บริการแล้ว) ความเข้ากันได้รวมถึงคะแนนเหล่านั้น:

  • ความเข้ากันได้ของแหล่งที่มา (ดีที่มี ... )
  • ความเข้ากันได้แบบไบนารี (ต้องมี!)
  • ความเข้ากันได้โยกย้าย
    • โปรแกรมที่มีอยู่ต้องทำงานต่อไป
    • ไลบรารีที่มีอยู่ต้องสามารถใช้ชนิดทั่วไปได้
    • จำเป็นต้องมี!

คำตอบเดิม:

ดังนั้น:

new ArrayList<String>() => new ArrayList()

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

ฉันควรจะพูดถึงcheckCollectionวิธีการของ Java 6 ซึ่งจะส่งกลับมุมมองที่ปลอดภัยแบบไดนามิกของคอลเลกชันที่ระบุ ความพยายามที่จะแทรกองค์ประกอบของความผิดประเภทใด ๆ ClassCastExceptionจะมีผลในทันที

กลไกยาชื่อสามัญในภาษาที่ให้เวลารวบรวม (คงที่) การตรวจสอบชนิด แต่มันก็เป็นไปได้ที่จะเอาชนะกลไกนี้มีบรรยากาศไม่ถูกตรวจสอบ

โดยทั่วไปนี่ไม่ใช่ปัญหาเนื่องจากคอมไพเลอร์ออกคำเตือนในการดำเนินการที่ไม่ได้ตรวจสอบทั้งหมด

อย่างไรก็ตามมีเวลาที่การตรวจสอบชนิดคงที่เพียงอย่างเดียวไม่เพียงพอเช่น:

  • เมื่อคอลเลกชันถูกส่งผ่านไปยังห้องสมุดบุคคลที่สามและมีความจำเป็นที่รหัสห้องสมุดไม่ได้เสียหายคอลเลกชันโดยการแทรกองค์ประกอบที่ผิดประเภท
  • โปรแกรมล้มเหลวด้วย a ClassCastExceptionซึ่งระบุว่าองค์ประกอบที่พิมพ์ไม่ถูกต้องถูกใส่ลงในคอลเลกชันที่กำหนดพารามิเตอร์ น่าเสียดายที่ข้อยกเว้นสามารถเกิดขึ้นได้ตลอดเวลาหลังจากที่ใส่องค์ประกอบที่ผิดพลาดดังนั้นโดยทั่วไปจะให้ข้อมูลเพียงเล็กน้อยหรือไม่มีเลยว่าเป็นแหล่งที่มาของปัญหาจริงหรือไม่

อัปเดตกรกฎาคม 2555 เกือบสี่ปีต่อมา:

ตอนนี้ (2012) มีรายละเอียดใน " กฎการเข้ากันได้ของ API การย้ายข้อมูล (การทดสอบลายเซ็น) "

ภาษาการเขียนโปรแกรมจาวาดำเนินการเรื่องข้อมูลทั่วไปโดยใช้การลบซึ่งทำให้มั่นใจได้ว่าเวอร์ชันดั้งเดิมและรุ่นทั่วไปจะสร้างไฟล์คลาสที่เหมือนกันยกเว้นข้อมูลเสริมบางอย่างเกี่ยวกับประเภท ความเข้ากันได้ของไบนารีไม่แตกเนื่องจากเป็นไปได้ที่จะแทนที่ไฟล์คลาสดั้งเดิมด้วยไฟล์คลาสทั่วไปโดยไม่ต้องเปลี่ยนหรือคอมไพล์รหัสไคลเอนต์ใหม่

เพื่ออำนวยความสะดวกในการเชื่อมต่อกับรหัสดั้งเดิมที่ไม่ใช่แบบทั่วไปคุณยังสามารถใช้การลบประเภทที่กำหนดพารามิเตอร์เป็นชนิดได้ ชนิดดังกล่าวเรียกว่าชนิดข้อมูลดิบ ( Java Language Specification 3 / 4.8 ) การอนุญาตประเภท raw ยังทำให้แน่ใจได้ถึงความเข้ากันได้ย้อนหลังสำหรับซอร์สโค้ด

ตามนี้java.util.Iteratorคลาสรุ่นต่อไปนี้เป็นทั้งไบนารีและซอร์สโค้ดที่เข้ากันได้แบบย้อนหลัง:

Class java.util.Iterator as it is defined in Java SE version 1.4:

public interface Iterator {
    boolean hasNext();
    Object next();
    void remove();
}

Class java.util.Iterator as it is defined in Java SE version 5.0:

public interface Iterator<E> {
    boolean hasNext();
    E next();
    void remove();
}

2
โปรดทราบว่าความเข้ากันได้ย้อนหลังนั้นสามารถทำได้โดยไม่มีการลบประเภท แต่ไม่ใช่หากไม่มีโปรแกรมเมอร์ Java ที่เรียนรู้ชุดสะสมใหม่ นั่นคือเส้นทางที่. NET ดำเนินไป กล่าวอีกนัยหนึ่งมันเป็นสัญลักษณ์แสดงหัวข้อย่อยที่สามซึ่งเป็นสิ่งสำคัญ (ต่อ)
Jon Skeet

15
โดยส่วนตัวฉันคิดว่านี่เป็นความผิดพลาดของสายตาสั้น - ให้ประโยชน์ระยะสั้นและเสียเปรียบในระยะยาว
Jon Skeet

8

การตอบสนองคำตอบของ Jon Skeet ที่ได้เสริมแล้ว ...

มีการกล่าวกันว่าการใช้ยาชื่อสามัญผ่านการลบทำให้เกิดข้อ จำกัด ที่น่ารำคาญ (เช่นไม่มีnew T[42]) นอกจากนี้ยังได้รับการกล่าวถึงว่าเหตุผลหลักในการทำสิ่งต่าง ๆ ในลักษณะนี้คือความเข้ากันได้ย้อนหลังในรหัสไบต์ นี่ก็เป็นจริงด้วยเช่นกัน bytecode ที่สร้างขึ้น - เป้าหมาย 1.5 ค่อนข้างแตกต่างจากการคัดเลือกนักแสดง -target 1.4 ในทางเทคนิคแล้วมันเป็นไปได้ (ผ่านกลอุบายอันยิ่งใหญ่) เพื่อเข้าถึงอินสแตนซ์ประเภททั่วไปที่รันไทม์พิสูจน์ว่ามีบางสิ่งในไบต์

จุดที่น่าสนใจมากขึ้น (ซึ่งยังไม่ได้รับการเพิ่มขึ้น) ก็คือการใช้ยาชื่อสามัญโดยใช้การลบทิ้งนั้นมีความยืดหยุ่นมากกว่าเล็กน้อยในสิ่งที่ระบบระดับสูงสามารถทำได้ ตัวอย่างที่ดีของเรื่องนี้คือการนำ JVM ไปใช้กับ CLR บน JVM เป็นไปได้ที่จะใช้ชนิดที่สูงกว่าโดยตรงเนื่องจากความจริงที่ว่า JVM เองไม่มีข้อ จำกัด ในประเภททั่วไป (เนื่องจาก "ประเภท" เหล่านี้ไม่มีประสิทธิภาพ) สิ่งนี้ตรงกันข้ามกับ CLR ซึ่งมีความรู้รันไทม์ของพารามิเตอร์อินสแตนซ์ ด้วยเหตุนี้ CLR เองจึงต้องมีแนวคิดว่าควรใช้ยาสามัญทั่วไปอย่างไรและพยายามลบล้างระบบโดยใช้กฎที่ไม่คาดคิด ผลที่ตามมาก็คือชนิดที่สูงขึ้นของ Scala บน CLR นั้นจะถูกนำไปใช้งานโดยใช้รูปแบบการลบแบบจำลองที่แปลกประหลาดภายในคอมไพเลอร์เอง

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


6
ความไม่สะดวกไม่ใช่เมื่อคุณต้องการทำสิ่ง "ซน" ในเวลาดำเนินการ เมื่อคุณต้องการทำสิ่งที่สมเหตุสมผลอย่างสมบูรณ์แบบในเวลาดำเนินการ อันที่จริงแล้วการลบประเภทช่วยให้คุณทำสิ่งที่ไกลเกินความจริงเช่นการเลือก List <String> ไปที่ List แล้วไปที่ List <Date> พร้อมคำเตือนเท่านั้น
Jon Skeet

5

ตามที่ฉันเข้าใจ (เป็น. NET guy) JVMไม่มีแนวคิดเกี่ยวกับ generics ดังนั้นคอมไพเลอร์จะแทนที่พารามิเตอร์ประเภทด้วย Object และดำเนินการร่ายทั้งหมดให้คุณ

ซึ่งหมายความว่า Java generics ไม่มีอะไรนอกจากไวยากรณ์น้ำตาลและไม่เสนอการปรับปรุงประสิทธิภาพสำหรับประเภทค่าที่ต้องใช้ Boxing / unboxing เมื่อผ่านการอ้างอิง


3
Java generics ไม่สามารถแสดงประเภทของค่าได้ - ไม่มีสิ่งใดในรายการ <int> อย่างไรก็ตามไม่มีการอ้างอิงแบบทีละตัวใน Java เลย - เป็นการส่งผ่านตามค่าอย่างเคร่งครัด (ซึ่งค่านั้นอาจเป็นข้อมูลอ้างอิง)
Jon Skeet

2

มีคำอธิบายที่ดี ฉันแค่เพิ่มตัวอย่างเพื่อแสดงว่าการลบประเภททำงานอย่างไรกับตัวถอดรหัส

ชั้นเรียนดั้งเดิม

import java.util.ArrayList;
import java.util.List;


public class S<T> {

    T obj; 

    S(T o) {
        obj = o;
    }

    T getob() {
        return obj;
    }

    public static void main(String args[]) {
        List<String> list = new ArrayList<>();
        list.add("Hello");

        // for-each
        for(String s : list) {
            String temp = s;
            System.out.println(temp);
        }

        // stream
        list.forEach(System.out::println);
    }
}

โค้ดที่ถอดรหัสจากไบต์ของมัน

import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Objects;
import java.util.function.Consumer;

public class S {

   Object obj;


   S(Object var1) {
      this.obj = var1;
   }

   Object getob() {
      return this.obj;
   }

   public static void main(String[] var0) {

   ArrayList var1 = new ArrayList();
   var1.add("Hello");


   // for-each
   Iterator iterator = var1.iterator();

   while (iterator.hasNext()) {
         String string;
         String string2 = string = (String)iterator.next();
         System.out.println(string2);
   }


   // stream
   PrintStream printStream = System.out;
   Objects.requireNonNull(printStream);
   var1.forEach(printStream::println);


   }
}

2

ทำไมต้องใช้ Generices

สั้น ๆ generics เปิดใช้งานประเภท (คลาสและอินเทอร์เฟซ) เป็นพารามิเตอร์เมื่อกำหนดคลาสอินเทอร์เฟซและวิธีการ เช่นเดียวกับพารามิเตอร์ทางการที่คุ้นเคยมากขึ้นที่ใช้ในการประกาศเมธอดพารามิเตอร์ชนิดเป็นวิธีการให้คุณใช้รหัสเดียวกันกับอินพุตที่ต่างกันอีกครั้ง ความแตกต่างคืออินพุตของพารามิเตอร์ที่เป็นทางการคือค่าในขณะที่อินพุตเพื่อพิมพ์พารามิเตอร์เป็นประเภท บทกวีที่ใช้ generics มีประโยชน์มากมายกว่ารหัสที่ไม่ใช่แบบทั่วไป:

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

การลบประเภทคืออะไร

Generics ถูกนำมาใช้กับภาษา Java เพื่อให้การตรวจสอบประเภทที่เข้มงวดมากขึ้นในเวลารวบรวมและเพื่อสนับสนุนการเขียนโปรแกรมทั่วไป ในการใช้งาน generics คอมไพเลอร์ Java ใช้การลบประเภทกับ:

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

[NB] - บริดจ์คืออะไร? ในไม่ช้าในกรณีของอินเทอร์เฟซแบบมีพารามิเตอร์เช่นComparable<T>นี้อาจทำให้วิธีการเพิ่มเติมจะถูกแทรกโดยคอมไพเลอร์ วิธีการเพิ่มเติมเหล่านี้เรียกว่าบริดจ์

วิธีลบการทำงาน

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

  • การลบList<Integer>, List<String>และเป็นList<List<String>>List
  • การลบคือList<Integer>[]List[]
  • การลบของListตัวเองเช่นเดียวกับประเภทดิบใด ๆ
  • การลบของ int นั้นคล้ายกับชนิดดั้งเดิมใด ๆ
  • การลบของIntegerตัวเองคล้ายกันสำหรับประเภทใด ๆ โดยไม่มีพารามิเตอร์ประเภท
  • การลบTในคำจำกัดความของasListคือObjectเพราะT ไม่มีขอบเขต
  • การลบTในความหมายของการmaxมีที่Comparableเพราะได้ผูกพันT Comparable<? super T>
  • การลบออกTในคำจำกัดความสุดท้ายของmaxคือObjectเพราะ TมีผลผูกพันObject& Comparable<T>และเราทำการลบทิ้งขอบเขตซ้ายสุด

ต้องระวังเมื่อใช้ยาชื่อสามัญ

ใน Java สองวิธีที่แตกต่างไม่สามารถมีลายเซ็นเดียวกัน เนื่องจากยาชื่อสามัญจะถูกนำมาใช้โดยการลบก็ยังตามด้วยวิธีการที่แตกต่างกันสองวิธีไม่สามารถมีลายเซ็นที่มีการลบแบบเดียวกัน คลาสไม่สามารถโหลดสองเมธอดที่มีลายเซ็นมีการลบแบบเดียวกันและคลาสไม่สามารถใช้อินเทอร์เฟซสองตัวที่มีการลบแบบเดียวกันได้

    class Overloaded2 {
        // compile-time error, cannot overload two methods with same erasure
        public static boolean allZero(List<Integer> ints) {
            for (int i : ints) if (i != 0) return false;
            return true;
        }
        public static boolean allZero(List<String> strings) {
            for (String s : strings) if (s.length() != 0) return false;
            return true;
        }
    }

เราตั้งใจให้รหัสนี้ทำงานดังนี้:

assert allZero(Arrays.asList(0,0,0));
assert allZero(Arrays.asList("","",""));

อย่างไรก็ตามในกรณีนี้การลบลายเซ็นของทั้งสองวิธีจะเหมือนกัน:

boolean allZero(List)

ดังนั้นจึงมีการรายงานการปะทะกันของชื่อในเวลารวบรวม มันเป็นไปไม่ได้ที่จะให้ทั้งสองวิธีชื่อเดียวกันและพยายามแยกแยะระหว่างพวกเขาโดยการบรรทุกเกินพิกัดเพราะหลังจากลบแล้วมันเป็นไปไม่ได้ที่จะแยกแยะวิธีการหนึ่งเรียกจากอีกวิธีหนึ่ง

หวังว่าผู้อ่านจะเพลิดเพลินไปกับ :)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.