อาร์กิวเมนต์สุดท้ายในวิธีการเชื่อมต่อ - ประเด็นคืออะไร?


192

ใน Java การกำหนดfinalอาร์กิวเมนต์ในเมธอดอินเทอร์เฟซนั้นถูกกฎหมายอย่างสมบูรณ์และไม่ปฏิบัติตามในคลาสการนำไปใช้งานเช่น:

public interface Foo {
    public void foo(int bar, final int baz);
}

public class FooImpl implements Foo {

    @Override
    public void foo(final int bar, int baz) {
        ...
    }
}

ในตัวอย่างข้างต้นbarและbazมีfinalคำจำกัดความที่ตรงกันข้ามในคลาส VS อินเทอร์เฟซ

ในทำนองเดียวกันไม่มีการfinalบังคับใช้ข้อ จำกัด ใด ๆ เมื่อเมธอดคลาสหนึ่งขยายอีกวิธีหนึ่งabstractหรือไม่ก็ได้

ในขณะที่finalมีค่าที่ใช้งานได้จริงบางส่วนในตัวเมธอดคลาสมีจุดใดบ้างที่ระบุfinalสำหรับพารามิเตอร์วิธีการอินเทอร์เฟซ?


finalไม่ได้ทำอะไรกับประเภทเนทีฟเนื่องจากถูกคัดลอก
Paul Tomblin

11
เพื่อเป็นประเด็นในการอภิปราย: ฉันเพิ่งลองใช้และหากสองinterfaceคำจำกัดความแตกต่างกันเฉพาะในfinalแอตทริบิวต์ของอาร์กิวเมนต์.classไฟล์ที่ได้จะเป็นแบบไบต์ต่อไบต์เหมือนกัน (และแน่นอนว่าjavap -vจะให้ผลลัพธ์เดียวกัน) เช่นเดียวกับสองคลาสที่แตกต่างกันในfinalแอตทริบิวต์เท่านั้น!
Joachim Sauer

2
@Paul: มันทำสิ่งเดียวกันกับประเภทการอ้างอิงทุกประการ: มันป้องกันไม่ให้อาร์กิวเมนต์ถูกแก้ไข (หากใช้ในการนำไปใช้งาน)
Joachim Sauer

มีความเกี่ยวข้องมากพอ ๆ กับสาธารณะในลายเซ็นของวิธีการ
Robin

2
@ ดีพัค: ฉันเห็นคุณขอตัวอย่างการทำงานเกี่ยวกับคำถามทุกประเภทแม้ว่ามันจะดูไม่ค่อยสมเหตุสมผลก็ตาม ฉันคิดว่าคุณควรลองเรียนรู้การคิดเชิงนามธรรม: ลองคิดถึงปัญหาโดยไม่ต้องมีโค้ดปฏิบัติการอยู่ตรงหน้าคุณ มันจะช่วยคุณได้มากในระยะยาว
Joachim Sauer

คำตอบ:


108

ดูเหมือนไม่มีประเด็นอะไรเลย ตามข้อกำหนดภาษา Java 4.12.4 :

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

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


พารามิเตอร์ method มีคุณสมบัติเป็นตัวแปรหรือไม่ เห็นได้ชัดว่าเป็นไปในทางปฏิบัติ แต่มันอยู่ในบริบทของข้อกำหนดหรือไม่?
mindas

11
ไม่สามารถใช้เพื่อจับคู่ลายเซ็นได้เนื่องจากไม่ปรากฏในไฟล์. class จริง มีไว้สำหรับคอมไพเลอร์เท่านั้น
Robin

@mindas - JLS กล่าวว่ามีเจ็ดชนิดของตัวแปร Method paramaters เป็นอันดับสี่ในรายการ
Ted Hopp

3
เอกสารประกอบจะไร้ประโยชน์อย่างไรก็ตามเนื่องจากfinalตัวปรับเปลี่ยนไม่ได้บังคับใช้กับคลาสการนำไปใช้งาน ลายเซ็นอินเทอร์เฟซของคุณอาจโกหกได้
Stefan Haberl

ในตอนนี้ข้อมูลจำเพาะภาษา Java 8 บอกว่ามีตัวแปรแปดชนิด (เพิ่มขึ้นจากเจ็ดตัว - เพิ่มพารามิเตอร์แลมบ์ดา ) พารามิเตอร์วิธีการยังคงเป็นอันดับสี่ในรายการ (อย่างน้อยก็มีบางสิ่งในชีวิตที่มั่นคง :-))
Ted Hopp

25

IDE บางตัวจะคัดลอกลายเซ็นของเมธอดนามธรรม / อินเทอร์เฟซเมื่อแทรกเมธอดการนำไปใช้ในคลาสย่อย

ฉันไม่เชื่อว่ามันสร้างความแตกต่างให้กับคอมไพเลอร์

แก้ไข: แม้ว่าฉันจะเชื่อว่าสิ่งนี้เป็นเรื่องจริงในอดีต แต่ฉันไม่คิดว่า IDE ในปัจจุบันจะทำเช่นนี้อีกต่อไป


2
จุดที่ถูกต้องแม้ว่าฉันไม่คิดว่าจะมี IDE มากมายเมื่อมีการใช้งานคุณลักษณะนี้ (หรือปล่อยทิ้งไว้โดยไม่ตั้งใจ) :-)
mindas

2
ฉันคิดว่าอยู่ในหมวดหมู่ของstatic transientเขตข้อมูล ;)
Peter Lawrey

1
และนักสร้างสาธารณะในชั้นเรียนนามธรรม
Peter Lawrey

1
IntelliJ ทำสิ่งนี้ เวอร์ชันของฉันคือ IntelliJ IDEA 2016.1.3 Build # IU-145.1617
Dormouse

1
@Dormouse น่าสนใจเพราะ Android Studio (3.1.4 Build # AI-173.4907809) ไม่ :(
The Godfather

18

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


7

อัปเดต:คำตอบเดิมด้านล่างถูกเขียนขึ้นโดยไม่เข้าใจคำถามทั้งหมดดังนั้นจึงไม่ได้กล่าวถึงคำถามโดยตรง:)อย่างไรก็ตามต้องเป็นข้อมูลสำหรับผู้ที่ต้องการเข้าใจการใช้finalคำหลักทั่วไป

สำหรับคำถามฉันต้องการอ้างความคิดเห็นของตัวเองจากด้านล่าง

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

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

ก. finalไม่อนุญาตให้ใช้คำหลักสำหรับอาร์กิวเมนต์เมธอดอินเทอร์เฟซ (นามธรรม) (แต่คุณสามารถใช้ในการนำไปใช้งานได้) หรือ
b. การประกาศอาร์กิวเมนต์เช่นเดียวกับfinalในอินเทอร์เฟซจะบังคับให้มีการประกาศfinalในการนำไปใช้งาน (แต่ไม่บังคับสำหรับรอบชิงชนะเลิศ)


ฉันนึกถึงเหตุผลสองประการที่ทำให้ลายเซ็นวิธีมีfinalพารามิเตอร์ได้: ถั่วและวัตถุ ( อันที่จริงทั้งคู่มีเหตุผลเดียวกัน แต่มีบริบทที่แตกต่างกันเล็กน้อย )

วัตถุ:

public static void main(String[] args) {
    StringBuilder cookingPot = new StringBuilder("Water ");
    addVegetables(cookingPot);
    addChicken(cookingPot);
    System.out.println(cookingPot.toString());
    // ^--- OUTPUT IS: Water Carrot Broccoli Chicken ChickenBroth 
    //      We forgot to add cauliflower. It went into the wrong pot.
}

private static void addVegetables(StringBuilder cookingPot) {
    cookingPot.append("Carrot ");
    cookingPot.append("Broccoli ");
    cookingPot = new StringBuilder(cookingPot.toString());
    //   ^--- Assignment allowed...
    cookingPot.append("Cauliflower ");
}

private static void addChicken(final StringBuilder cookingPot) {
    cookingPot.append("Chicken ");
    //cookingPot = new StringBuilder(cookingPot.toString());
    //     ^---- COMPILATION ERROR! It is final.
    cookingPot.append("ChickenBroth ");
}

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

ถั่ว: มันเป็นแนวคิดเดียวกับวัตถุ(ตามที่แสดงไว้ด้านบน) ถั่วเป็นหลักObjectใน Java อย่างไรก็ตามถั่ว (JavaBeans) ถูกใช้ในแอพพลิเคชั่นต่างๆเพื่อเป็นวิธีที่สะดวกในการจัดเก็บและส่งต่อชุดข้อมูลที่เกี่ยวข้องที่กำหนดไว้ เช่นเดียวกับaddVegetablesที่จะทำได้เลอะกระบวนการปรุงอาหารโดยการสร้างหม้อหุงต้มใหม่StringBuilderและทิ้งมันไปกับกะหล่ำก็ยังสามารถทำเช่นเดียวกันกับหม้อปรุงอาหารJavaBean


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

ฉันเชื่อว่าคุณไม่ได้ถูกบังคับให้ดำเนินการขั้นสุดท้ายของการโต้แย้งเพื่อปล่อยให้คุณมีอิสระในการตัดสินใจว่าควรถือเป็นที่สิ้นสุดหรือไม่ในการนำไปใช้ของคุณเอง แต่ใช่มันฟังดูแปลกที่คุณสามารถประกาศfinalในอินเทอร์เฟซได้ แต่ขอให้มันไม่ใช่ขั้นสุดท้ายในการนำไปใช้งาน จะมีเหตุผลมากกว่านี้หากไม่อนุญาตให้ใช้คีย์เวิร์ด(a) finalสำหรับอาร์กิวเมนต์เมธอด interface (นามธรรม) (แต่คุณสามารถใช้ในการนำไปใช้งานได้) หรือ(b) การประกาศอาร์กิวเมนต์finalในอินเทอร์เฟซจะบังคับให้ประกาศfinalในการนำไปใช้งาน (แต่ไม่บังคับสำหรับรอบชิงชนะเลิศ)
ADTC

"มันไม่ใช่คำตอบที่ถูกต้องจริงๆสำหรับคำถาม"คุณพูดถูกฉันไม่รู้ว่าฉันคิดอะไรอยู่ ... คงจะเป็นช่วงต้นเดือนกรกฎาคมหรืออะไรสักอย่าง :)
ADTC

2

ฉันเชื่อว่ามันอาจเป็นรายละเอียดที่ไม่จำเป็นเพราะไม่ว่าจะเป็นขั้นสุดท้ายหรือไม่เป็นรายละเอียดการนำไปใช้งาน

(เช่นเดียวกับการประกาศวิธีการ / สมาชิกในอินเทอร์เฟซเป็นสาธารณะ)

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