วิธีแคสรายการ <Object> ไปยัง List <MyClass>


100

ไม่ได้รวบรวมข้อเสนอแนะใด ๆ ที่ชื่นชม

 ...
  List<Object> list = getList();
  return (List<Customer>) list;

คอมไพเลอร์กล่าวว่า: ไม่สามารถส่งList<Object>ไปยังList<Customer>


คำตอบ:


157

คุณสามารถโยนวัตถุใด ๆ ให้เป็นประเภทใดก็ได้โดยการอัพลงวัตถุก่อน ในกรณีของคุณ:

(List<Customer>)(Object)list; 

คุณต้องแน่ใจว่าในรันไทม์รายการไม่มีอะไรเลยนอกจากอ็อบเจ็กต์ของลูกค้า

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


8
@SuppressWarnings("unchecked")นี้ยังมีความต้องการ โปรดทราบว่าคุณยังสามารถที่จะ upCast แทนที่จะเป็น(List) (Object)
200_success

36

นั่นเป็นเพราะแม้ว่าลูกค้าจะเป็นวัตถุ แต่รายชื่อลูกค้าไม่ใช่รายการวัตถุ หากเป็นเช่นนั้นคุณสามารถใส่วัตถุใดก็ได้ในรายชื่อลูกค้า


2
ไม่มันไม่ใช่ คุณจะหลีกเลี่ยงความปลอดภัยประเภทหากได้รับอนุญาตข้างต้น โปรดทราบว่าการแคสต์ไม่ได้หมายถึงการสร้างรายการใหม่และคัดลอกรายการ หมายถึงจัดการอินสแตนซ์เดียวเป็นประเภทอื่นดังนั้นคุณจะมีรายการที่มีออบเจ็กต์ที่อาจไม่ใช่ลูกค้าพร้อมการรับประกันความปลอดภัยประเภทที่ไม่ควร สิ่งนี้ไม่มีส่วนเกี่ยวข้องกับ java เช่นนี้ เนื่องจากคุณรู้สึกว่าคุณลักษณะนี้ทำให้ Java ติดอยู่ในยุคมืดฉันขอท้าให้คุณอธิบายว่าคุณคิดว่าสิ่งนี้ควรใช้งานได้อย่างไร
Lasse V.Karlsen

1
@ BrainSlugs83 สำหรับความต้องการของคุณ (รายการ <ลูกค้า>) (วัตถุ) รายการ;
Muthu Ganapathy Nathan

@ LasseV.Karlsen ฉันไม่ได้พูดถึงการคัดเลือกนักแสดงฉันกำลังพูดถึงการเปลี่ยนใจเลื่อมใส มันจะไม่หลีกเลี่ยงความปลอดภัยประเภทมันจะบังคับใช้ การใช้งานทั่วไปของ Java การขาดตัวดำเนินการมากเกินไปวิธีการขยายและสิ่งอำนวยความสะดวกสมัยใหม่อื่น ๆ อีกมากมายทำให้ฉันผิดหวังอย่างมาก - ในขณะเดียวกัน. NET มีวิธีการขยายที่แยกจากกันสองวิธีสำหรับวิธีนี้หนึ่งเรียกว่า.Cast<T>()และอีกวิธีหนึ่งเรียกว่า.OfType<T>(). ก่อนหน้านี้ทำการแคสต์ในแต่ละองค์ประกอบ (โยนข้อยกเว้นที่ต้องการ) ในขณะที่องค์ประกอบหลังจะกรององค์ประกอบที่ไม่สามารถแคสต์ได้ (ดังนั้นคุณจะเลือกอย่างใดอย่างหนึ่งขึ้นอยู่กับสถานการณ์การใช้งานของคุณ)
BrainSlugs83

1
@EAGER_STUDENT ฉันจะไม่ใส่มันผ่าน Java ที่อาจใช้งานได้จริง (ธุรกิจลบประเภทที่ไร้สาระทั้งหมดนี้ฉันต้องลอง ... ) - แต่ไม่ฉันจะไม่เขียนโค้ดแบบนั้น - คุณแพ้ พิมพ์ความปลอดภัยจะเกิดอะไรขึ้นหากองค์ประกอบหนึ่งในคอลเลกชันไม่ใช่instanceofลูกค้า
BrainSlugs83

1
@ BrainSlugs83 คนที่ตั้งคำถามถามถึงความหล่อโดยเฉพาะ และหาก Java ยังไม่มีเมธอดที่เกี่ยวข้องเช่นเมธอด. NET ที่คุณอ้างถึง (ซึ่งยังไม่สามารถแปลง btw ได้) คุณก็จะสามารถเพิ่มได้อย่างง่ายดาย ทั้งหมดนี้ค่อนข้างตรงกับคำถามที่ถามเกี่ยวกับไวยากรณ์ที่เฉพาะเจาะจง
Lasse V.Karlsen

35

คำตอบที่ดีที่สุดอาจแตกต่างกันไปทั้งนี้ขึ้นอยู่กับรหัสอื่นของคุณ ลอง:

List<? extends Object> list = getList();
return (List<Customer>) list;

หรือ

List list = getList();
return (List<Customer>) list;

แต่โปรดทราบว่าไม่แนะนำให้ทำการร่ายแบบไม่เลือก


3
-1 - นี่เป็นนิสัยที่ไม่ดีจริงๆที่จะเข้ามาและถ้าคุณไม่ใช้คำอธิบายประกอบ "ไม่ได้ตรวจสอบ" คอมไพเลอร์จะยังคงบ่นเกี่ยวกับเรื่องนี้
kdgregory

25

ด้วย Java 8 Streams :

บางครั้งการหล่อด้วยกำลังเดรัจฉานก็ใช้ได้:

List<MyClass> mythings = (List<MyClass>) (Object) objects

แต่นี่เป็นวิธีแก้ปัญหาที่หลากหลายกว่า:

List<Object> objects = Arrays.asList("String1", "String2");

List<String> strings = objects.stream()
                       .map(element->(String) element)
                       .collect(Collectors.toList());

มีประโยชน์มากมาย แต่อย่างหนึ่งก็คือคุณสามารถแคสต์รายการของคุณให้หรูหรายิ่งขึ้นหากคุณไม่แน่ใจว่ามีอะไรบ้าง:

objects.stream()
    .filter(element->element instanceof String)
    .map(element->(String)element)
    .collect(Collectors.toList());

1
นั่นเป็นของสำเนามากกว่านักแสดง
200_success

การคัดเลือกนักแสดงที่กล่าวถึงใน anser ที่ได้รับการยอมรับไม่ได้ผลสำหรับฉัน นอกจากนี้ฉันยังใช้ Java 7 แต่ฝรั่งก็FluentIterableใช้ได้ผลกับฉัน
Sridhar Sarnobat

นี่คือสิ่งที่ฉันกำลังมองหาฉันไม่รู้ไวยากรณ์
Fueled By Coffee


8

โปรดทราบว่าฉันไม่ใช่โปรแกรมเมอร์ java แต่ใน. NET และ C # คุณลักษณะนี้เรียกว่าความแตกต่างหรือความแปรปรวนร่วม ฉันยังไม่ได้เจาะลึกสิ่งเหล่านั้นเนื่องจากเป็นสิ่งใหม่ใน. NET 4.0 ซึ่งฉันไม่ได้ใช้เนื่องจากเป็นเพียงเบต้าดังนั้นฉันไม่รู้ว่าคำศัพท์ใดในสองคำที่อธิบายปัญหาของคุณ แต่ให้ฉันอธิบาย ปัญหาทางเทคนิคเกี่ยวกับเรื่องนี้

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

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

นี่คือปัญหาที่เกิดขึ้น

จะเกิดอะไรขึ้นถ้าสิ่งต่อไปนี้ได้รับอนุญาต (โปรดทราบว่าฉันคิดว่าก่อนที่จะแคสต์รายการวัตถุมีเฉพาะวัตถุของลูกค้าเท่านั้นมิฉะนั้นการแคสต์จะไม่ทำงานแม้ใน java เวอร์ชันสมมุตินี้):

List<Object> list = getList();
List<Customer> customers = (List<Customer>)list;
list.Insert(0, new someOtherObjectNotACustomer());
Customer c = customers[0];

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

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

ใน. NET 4.0 (ฉันรู้ว่าคำถามของคุณเกี่ยวกับ java) สิ่งนี้จะได้รับอนุญาตในบางกรณีที่เฉพาะเจาะจงซึ่งคอมไพเลอร์สามารถรับประกันได้ว่าการดำเนินการที่คุณทำนั้นปลอดภัย แต่โดยทั่วไปแล้วการส่งประเภทนี้จะไม่ ได้รับอนุญาต เช่นเดียวกันกับ java แม้ว่าฉันจะไม่แน่ใจเกี่ยวกับแผนการใด ๆ ที่จะแนะนำการใช้ร่วมและความแตกต่างกับภาษา java

หวังว่าจะมีคนที่มีความรู้ java ดีกว่าฉันสามารถบอกรายละเอียดเกี่ยวกับ java ในอนาคตหรือการนำไปใช้งานได้


3
อนาคตของ Java คือ ... Scala อย่างจริงจังคนที่ใช้ภาษาทั่วไปใน Java ได้พัฒนาภาษาใหม่ที่คล้ายกับ Java อย่างคลุมเครือ แต่จริงๆแล้วมีความเชี่ยวชาญในประเภทต่างๆ: การใช้งานการจัดการประเภทอย่างละเอียดและสม่ำเสมอ ฉันคิดว่าไม่มีใครรู้แน่ชัดว่าฟีเจอร์ใดของ Scala จะกลับเข้าสู่ Java และเมื่อใด
Carl Smotricz

คำอธิบายที่ยอดเยี่ยมเกี่ยวกับความแปรปรวนร่วมที่ตอบคำถาม OPs อย่างแท้จริง ทำได้ดี.
Kevin Day

Carl: ฉันคิดว่านักพัฒนา Java บางคนเดินหน้าสร้าง C #? :) อย่างไรก็ตามใช่แล้ว Java มักจะไปสู่ทิศทางของ Scala ในอนาคตแทนที่จะพูดว่ามีบางอย่างที่พิมพ์น้อยกว่า
Esko

@Carl - ใน Scala มีความแตกต่างเล็กน้อยโดยค่าเริ่มต้นรายการจะไม่เปลี่ยนรูป ดังนั้นโดยทั่วไปโย่ไม่ได้มีปัญหาของการเพิ่มวัตถุไปยังรายการของลูกค้าตั้งแต่เมื่อคุณทำเช่นนี้คุณจะได้รับใหม่รายชื่อของวัตถุ
Brian Agnew

เอ่อ ... ในทางเทคนิคถูกต้อง - แต่ก่อนหน้านี้. NET 4.0 คุณสามารถทำได้ด้วยวิธีการขยาย IEnumerable ปกติ (.Cast <> และ. คุณแค่ต้องการการทำซ้ำประเภทที่แข็งแกร่ง
BrainSlugs83

7

อีกวิธีหนึ่งคือการใช้สตรีม java 8

    List<Customer> customer = myObjects.stream()
                                  .filter(Customer.class::isInstance)
                                  .map(Customer.class::cast)
                                  .collect(toList());

1
ขอบคุณโซลูชันนี้สวยงามมากโดยใช้วิธีการอ้างอิง
thang

1
ไม่เคยคิดว่าจะมีคนเลื่อนลงไปไกลขนาดนั้น: D
d0x

3

คุณควรทำซ้ำในรายการและโยน Objects ทั้งหมดทีละรายการ


3

คุณสามารถทำสิ่งนี้ได้

List<Customer> cusList = new ArrayList<Customer>();

for(Object o: list){        
    cusList.add((Customer)o);        
}

return cusList; 

หรือวิธี Java 8

list.stream().forEach(x->cusList.add((Customer)x))

return cuslist;

2

คุณไม่สามารถเพราะList<Object>และList<Customer>ไม่ได้อยู่ในโครงสร้างมรดกเดียวกัน

คุณสามารถเพิ่มตัวสร้างใหม่ในList<Customer>ชั้นเรียนของคุณโดยใช้เวลา a List<Object>แล้ววนซ้ำผ่านรายการที่แคสต์แต่ละรายการObjectไปยัง a Customerและเพิ่มลงในคอลเล็กชันของคุณ โปรดทราบว่าข้อยกเว้นในการแคสต์ที่ไม่ถูกต้องอาจเกิดขึ้นได้หากผู้โทรList<Object>มีสิ่งที่ไม่ใช่ไฟล์Customer.

จุดสำคัญของรายการทั่วไปคือการ จำกัด รายการบางประเภท คุณกำลังพยายามเอารายชื่อที่สามารถมีอะไรก็ได้ (คำสั่งซื้อสินค้า ฯลฯ ) และบีบให้เป็นรายการที่สามารถรับลูกค้าได้เท่านั้น


2

คุณสามารถสร้างรายการใหม่และเพิ่มองค์ประกอบเข้าไปได้:

ตัวอย่างเช่น:

List<A> a = getListOfA();
List<Object> newList = new ArrayList<>();
newList.addAll(a);

1

ทางออกที่ดีที่สุดของคุณคือการสร้างใหม่List<Customer>ทำซ้ำList<Object>เพิ่มแต่ละรายการในรายการใหม่แล้วส่งคืน


1

ตามที่คนอื่น ๆ ชี้ให้เห็นคุณไม่สามารถโยนพวกเขาได้อย่างปลอดภัยเนื่องจากList<Object>ไม่ใช่ไฟล์List<Customer>. สิ่งที่คุณทำได้คือกำหนดมุมมองในรายการที่ทำการตรวจสอบชนิดในสถานที่ การใช้Google Collectionsที่จะเป็น:

return Lists.transform(list, new Function<Object, Customer>() {
  public Customer apply(Object from) {
    if (from instanceof Customer) {
      return (Customer)from;
    }
    return null; // or throw an exception, or do something else that makes sense.
  }
});

1

คล้ายกับ Bozho ด้านบน คุณสามารถแก้ปัญหาบางอย่างได้ที่นี่ (แม้ว่าฉันจะไม่ชอบก็ตาม) ด้วยวิธีนี้:

public <T> List<T> convert(List list, T t){
    return list;
}

ใช่. มันจะส่งรายการของคุณเป็นประเภททั่วไปที่คุณต้องการ

ในกรณีที่ระบุข้างต้นคุณสามารถทำโค้ดได้ดังนี้:

    List<Object> list = getList();
    return convert(list, new Customer());

ฉันชอบวิธีนี้ แม้ว่าคุณจะต้องเพิ่ม SuppressWarnings แต่การเพิ่มในที่เดียวจะดีกว่าในการหล่อที่ไม่ปลอดภัยทุกครั้ง
robson

1

ขึ้นอยู่กับสิ่งที่คุณต้องการทำกับรายการคุณอาจไม่จำเป็นต้องแคสต์ไปยังไฟล์List<Customer>. หากคุณต้องการเพิ่มCustomerวัตถุในรายการคุณสามารถประกาศได้ดังนี้:

...
List<Object> list = getList();
return (List<? super Customer>) list;

นี่เป็นสิ่งที่ถูกกฎหมาย (ไม่ใช่แค่ถูกกฎหมาย แต่ถูกต้อง - รายการเป็น "supertype บางอย่างให้กับลูกค้า") และหากคุณกำลังจะส่งต่อไปยังวิธีการที่จะเป็นเพียงการเพิ่มออบเจ็กต์ลงในรายการดังกล่าวข้างต้น ขอบเขตทั่วไปเพียงพอสำหรับสิ่งนี้

ในทางกลับกันหากคุณต้องการดึงวัตถุจากรายการและพิมพ์ให้พวกเขาพิมพ์เป็นลูกค้าอย่างรุนแรงคุณก็โชคไม่ดีและถูกต้อง เนื่องจากรายการนี้List<Object>ไม่มีการรับประกันว่าเนื้อหาจะเป็นของลูกค้าดังนั้นคุณจะต้องจัดเตรียมการคัดเลือกนักแสดงของคุณเองในการดึงข้อมูล (หรืออย่างแน่นอนให้แน่ใจว่ารายการจะมีCustomersและใช้ double-cast จากคำตอบอื่น ๆ เท่านั้น แต่โปรดทราบว่าคุณกำลังหลีกเลี่ยงความปลอดภัยในการคอมไพล์ไทม์ที่คุณได้รับจากยาสามัญในนี้ กรณี).

การพูดอย่างกว้าง ๆ ควรพิจารณาขอบเขตทั่วไปที่กว้างที่สุดเท่าที่จะเป็นไปได้ซึ่งเป็นที่ยอมรับได้เมื่อเขียนวิธีการเป็นสองเท่าดังนั้นหากจะใช้เป็นวิธีการของห้องสมุด หากคุณกำลังจะอ่านจากรายการเท่านั้นให้ใช้List<? extends T>แทนList<T>เช่นซึ่งจะช่วยให้ผู้โทรของคุณมีขอบเขตมากขึ้นในข้อโต้แย้งที่พวกเขาสามารถส่งผ่านได้และหมายความว่าพวกเขามีโอกาสน้อยที่จะประสบปัญหาที่หลีกเลี่ยงได้คล้ายกับที่คุณ ' กำลังมีที่นี่

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