Java Generics Wildcarding พร้อมคลาสหลายคลาส


385

ฉันต้องการมีคลาสวัตถุ แต่ฉันต้องการบังคับให้คลาสใดก็ตามที่แสดงถึงการขยายคลาส A และใช้อินเตอร์เฟส B

ที่ฉันสามารถทำได้:

Class<? extends ClassA>

หรือ:

Class<? extends InterfaceB>

แต่ฉันทำทั้งสองอย่างไม่ได้ มีวิธีทำเช่นนี้หรือไม่?

คำตอบ:


613

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

<T extends ClassA & InterfaceB>

ดูบทช่วยสอนทั่วไปที่ sun.com โดยเฉพาะอย่างยิ่งในส่วนพารามิเตอร์ชนิดที่ถูกผูกไว้ที่ด้านล่างของหน้า คุณสามารถแสดงรายการอินเทอร์เฟซได้มากกว่าหนึ่งรายการหากต้องการโดยใช้& InterfaceNameสำหรับแต่ละรายการที่คุณต้องการ

สิ่งนี้อาจมีความซับซ้อนตามอำเภอใจ ในการสาธิตให้ดูที่การประกาศ JavaDoc Collections#maxซึ่ง (ห่อลงบนสองบรรทัด) คือ:

public static <T extends Object & Comparable<? super T>> T
                                           max(Collection<? extends T> coll)

ทำไมจึงซับซ้อน ตามที่กล่าวใน Java Generics คำถามที่พบบ่อย: เพื่อรักษาความเข้ากันได้แบบไบนารี

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

class classB { }
interface interfaceC { }

public class MyClass<T extends classB & interfaceC> {
    Class<T> variable;
}

เพื่อให้ได้variableข้อ จำกัด ที่คุณต้องการ สำหรับข้อมูลเพิ่มเติมและตัวอย่างการตรวจสอบหน้า 3 ของGenerics ในชวา 5.0 หมายเหตุใน<T extends B & C>ชื่อคลาสจะต้องมาก่อนและอินเทอร์เฟซตาม และแน่นอนว่าคุณสามารถแสดงคลาสได้เพียงรายการเดียวเท่านั้น


94
สิ่งนี้มีประโยชน์ เป็นมูลค่าการกล่าวขวัญว่าชั้นจะต้องมาก่อนคุณไม่สามารถพูดว่า '<T ขยาย InterfaceB & ClassA>
EricS

5
คุณทำเช่นเดียวกันสำหรับ T ควรขยายคลาสหรือใช้อินเตอร์เฟสได้อย่างไร
Ragunath Jawahar

1
@RagunathJawahar: คุณไม่สามารถเปิดกว้างเกินไปเกี่ยวกับเรื่องนี้ คุณต้องมีขอบเขตและหนึ่งในนั้นคือรู้ล่วงหน้าว่าคุณจะมีส่วนต่อประสานและที่ที่คุณจะมีชั้นเรียนรวมถึงวิธีใช้มรดก คุณต้องรู้พารามิเตอร์ชนิดถ้ามันจะเป็นคลาสหรืออินเทอร์เฟซ
Eddie

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

1
ใช่มันไม่ใช่สัญลักษณ์แทนและคุณไม่สามารถทำสิ่งที่ OP ร้องขอด้วยสัญลักษณ์แทนได้ คุณสามารถทำสิ่งที่ OP ต้องการได้อย่างมีประสิทธิภาพไม่ใช่แค่สัญลักษณ์แทน
Eddie

17

คุณไม่สามารถทำได้ด้วยพารามิเตอร์ประเภท "ไม่ระบุชื่อ" (เช่นสัญลักษณ์ตัวแทนที่ใช้?) แต่คุณสามารถทำได้ด้วยพารามิเตอร์ประเภท "ชื่อ" เพียงประกาศพารามิเตอร์ประเภทที่วิธีหรือระดับชั้นเรียน

import java.util.List;
interface A{}
interface B{}
public class Test<E extends B & A, T extends List<E>> {
    T t;
}

2
ทำไมไม่อนุญาตให้ใช้สัญลักษณ์แทน เช่นฉันไม่เห็นว่าทำไมสิ่งนี้จึงไม่ถูกต้อง: ArrayList <? ขยายรายการ ClassA & InterfaceB>; และถ้าคุณดึงองค์ประกอบออกจากรายการคุณสามารถกำหนดให้กับตัวแปรประเภท ClassA หรือประเภท InterfaceB
ทำเครื่องหมาย

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