วิธีการทำให้วิธีการ Java Generic คงที่?


172

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

public class ArrayUtils<E> {

        public E[] appendToArray(E[] array, E item) {
            E[] result = (E[])new Object[array.length+1];
            result[array.length] = item;
            return result;
        }
}

คุณได้รับข้อผิดพลาดในการรวบรวมอะไร นอกจากนี้ทำไมไม่ใช้เพียงหนึ่งในไลบรารีคอนเทนเนอร์มาตรฐาน?
Karl Knechtel

1
ข้อผิดพลาดในการคอมไพล์: ฉันเพิ่มตัวแก้ไขสแตติกไม่ถูกต้องจริง ๆ .. การใช้คอลเล็กชัน: ใช่การใช้คอลเลกชันจะเหมาะ แต่คำถามไม่ได้เกี่ยวกับคอลเลกชัน vs อาร์เรย์ของกรณีการใช้ของฉันต้องมีอาร์เรย์
Chris Johnson

โปรดทราบว่าคุณจะต้องใช้การสะท้อนกลับ (EVIL) เพื่อหยุดรหัสลูกค้าที่เกิดข้อยกเว้นในบางกรณี แต่ไม่ใช่ทุกกรณี (ดี) เป็นการดีที่สุดที่จะหลีกเลี่ยงอาร์เรย์อ้างอิง
Tom Hawtin - tackline

คำตอบ:


283

สิ่งเดียวที่คุณทำได้คือเปลี่ยนลายเซ็นของคุณเป็น

public static <E> E[] appendToArray(E[] array, E item)

รายละเอียดที่สำคัญ:

นิพจน์ทั่วไปก่อนหน้าค่าที่ส่งคืนจะแนะนำ (ประกาศ) ตัวแปรชนิดทั่วไปใหม่

นอกจากนี้พิมพ์ตัวแปรระหว่างประเภท ( ArrayUtils) และวิธีการแบบคงที่ ( appendToArray) ไม่เคยรบกวนซึ่งกันและกัน

ดังนั้นสิ่งนี้หมายความว่า: ในคำตอบของฉัน<E>จะซ่อนEจากถ้าวิธีการที่จะไม่เป็นArrayUtils<E> staticและ<E>มีอะไรจะทำอย่างไรกับการจากEArrayUtils<E>

เพื่อสะท้อนความจริงนี้ดีกว่าคำตอบที่ถูกต้องจะเป็น:

public static <I> I[] appendToArray(I[] array, I item)

30
โปรดทราบว่ามีอย่างไม่มีความสัมพันธ์ระหว่างตัวแปรประเภทชั้นระดับและตัวแปรวิธีสถิตประเภทE Eฉันคิดว่าเป็นวิธีปฏิบัติที่ดีกว่ามากในการใช้ชื่อตัวแปรที่แตกต่างกันเมื่อประกาศวิธีทั่วไปแบบคงที่หรืออย่างอื่นภายในคลาสทั่วไป
ผู้พิพากษาจิต

แต่ในกรณีนี้ฉันสามารถส่งวัตถุหนึ่งชนิดที่แตกต่างกันไปใน params เช่นเดียวกับฉันสามารถส่งผ่านอาร์เรย์จำนวนเต็ม [] เป็นพารามิเตอร์แรกและรายการคู่
pinkpanther

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

80
public static <E> E[] appendToArray(E[] array, E item) { ...

<E>หมายเหตุ

วิธีการทั่วไปแบบคงที่ต้องมีการประกาศทั่วไปของตัวเอง ( public static <E>) แยกต่างหากจากการประกาศทั่วไปของชั้นเรียน ( public class ArrayUtils<E>)

หากคอมไพเลอร์บ่นเกี่ยวกับประเภทความกำกวมในการเรียกใช้วิธีการทั่วไปแบบสแตติก (ไม่น่าเป็นไปได้ในกรณีของคุณ แต่โดยทั่วไปแล้วพูดในกรณี) นี่คือวิธีการเรียกใช้วิธีแบบคงที่ทั่วไปอย่างชัดเจนโดยใช้ประเภทเฉพาะ ( _class_.<_generictypeparams_>_methodname_):

String[] newStrings = ArrayUtils.<String>appendToArray(strings, "another string");

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


10

คุณต้องย้ายพารามิเตอร์ type ไปที่ระดับเมธอดเพื่อระบุว่าคุณมีเมธอดทั่วไปมากกว่าคลาสทั่วไป:

public class ArrayUtils {
    public static <T> E[] appendToArray(E[] array, E item) {
        E[] result = (E[])new Object[array.length+1];
        result[array.length] = item;
        return result;
    }
}

1
สิ่งนี้จะไม่ทำงานเนื่องจากคุณไม่ได้กำหนดประเภททั่วไป E ในกรณีนี้คุณยังต้องมีประเภททั่วไป <E> ในการกำหนดชั้นเรียน
George Xavier

0

ฉันจะอธิบายอย่างง่าย ๆ

ยาสามัญที่กำหนดในระดับ Class จะแยกจากยาชื่อสามัญที่กำหนดในระดับวิธี (แบบคงที่)

class Greet<T> {

    public static <T> void sayHello(T obj) {
        System.out.println("Hello " + obj);
    }
}

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

class Greet<T> {

    public static <E> void sayHello(E obj) {
        System.out.println("Hello " + obj);
    }
}

ทำไมวิธีการแบบคงที่จำเป็นต้องมี generics ของตัวเองแยกต่างหากจากระดับ

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

ดังนั้นทุกครั้งที่คุณเรียกใช้เมธอดสแตติก

Greet.sayHello("Bob");
Greet.sayHello(123);

JVM ตีความมันดังต่อไปนี้

Greet.<String>sayHello("Bob");
Greet.<Integer>sayHello(123);

ทั้งสองให้ผลลัพธ์เดียวกัน

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