พฤติกรรมของวิธีการคงที่ขั้นสุดท้าย


124

ฉันเล่นกับโมดิฟายเออร์ด้วยวิธีการคงที่และเจอพฤติกรรมแปลก ๆ

อย่างที่เราทราบกันดีว่าเมธอดแบบคงที่ไม่สามารถแทนที่ได้เนื่องจากเกี่ยวข้องกับคลาสมากกว่าอินสแตนซ์

ดังนั้นหากฉันมีตัวอย่างด้านล่างนี้ก็จะรวบรวมได้ดี

//Snippet 1 - Compiles fine
public class A {
    static void ts() {
    }
}

class B extends A {
    static void ts() {
    }
}

แต่ถ้าฉันรวมตัวปรับแต่งขั้นสุดท้ายเป็นวิธีการแบบคงที่ในคลาส A การคอมไพล์ล้มเหลว ts () ใน B ไม่สามารถแทนที่ ts () ใน A; วิธีการแทนที่เป็นที่สิ้นสุดแบบคงที่

เหตุใดจึงเกิดขึ้นเมื่อวิธีการคงที่ไม่สามารถแทนที่ได้เลย


2
ดูเหมือนจะแปลก +1 สำหรับคำถาม แต่จนถึงขณะนี้ไม่มีคำตอบใดที่น่าพอใจ
Rakesh Juyal

1
มันไม่ได้ถูกแทนที่ ยังคงมีอยู่ที่ A.ts ()
Alex Feinman

คำตอบ:


166

วิธีการคงที่ไม่สามารถลบล้างได้ แต่สามารถซ่อนไว้ได้ ts()วิธีการของ B ไม่ได้เอาชนะ (ไม่อยู่ภายใต้ความแตกต่าง) ts()ของ แต่มันจะซ่อนมันไว้ ถ้าคุณเรียกts()ด้วย B (NOT A.ts()หรือB.ts()... just ts()) ตัวใดตัวหนึ่งของ B จะถูกเรียกและไม่ใช่ A เนื่องจากสิ่งนี้ไม่ได้อยู่ภายใต้ความหลากหลายการเรียกts()ใน A จะไม่ถูกเปลี่ยนเส้นทางไปที่ใน B

คีย์เวิร์ดfinalจะปิดการซ่อนเมธอด ดังนั้นจึงไม่สามารถซ่อนได้และการพยายามทำเช่นนั้นจะทำให้คอมไพเลอร์ผิดพลาด

หวังว่านี่จะช่วยได้


30
บางทีอาจจะเสร็จสิ้นคำตอบของคุณซึ่งถูกต้องฉันเชื่อว่าปัญหาที่นี่เป็นข้อความแสดงข้อผิดพลาดของคอมไพเลอร์ที่ไม่ดี: ควรระบุว่า B ไม่สามารถซ่อน ts () ใน A การประกาศวิธีการแบบคงที่ขั้นสุดท้ายเป็นการประกาศว่าไม่สามารถซ่อนได้
Sean Owen

2
@Sean Owen: ฉันก็คิดอย่างนั้นเหมือนกัน คำว่า 'ซ่อน' ยังใช้ในข้อกำหนดของ Java ดังนั้นทำไมไม่ใช้ในข้อความคอมไพเลอร์
NawaMan

2
เหตุใดจึงเป็นคุณลักษณะนี้ มันจะมีประโยชน์ในบริบทใด?
user253751

การทดสอบระดับสาธารณะ {โมฆะสาธารณะสุดท้ายคงที่หลัก (String ... srik) {System.out.println ("ในวิธีการหลัก"); ทีเอส (); } โมฆะคงที่สาธารณะ ts () {Child c = new Child (); c.ts (); System.out.println ("ทดสอบ ts"); }} คลาสสาธารณะ Child ขยายการทดสอบ {public static void ts () {System.out.println ("Child ts"); }} สวัสดีคุณช่วยอธิบายหน่อยได้ไหมว่าเกิดอะไรขึ้นในสถานการณ์นี้
srikanth r

@SeanOwen ฉันไม่คิดว่ามันถูกต้องเช่นกันคอมไพเลอร์ควรบอกว่าเนื่องจากA#tsมีการสืบทอดและมีวิธีการดังกล่าวอยู่แล้วBเพียงแค่มีสองวิธีที่มีลายเซ็นเดียวกันและตัวปรับแต่งอื่น ( final) จะไม่ทำงานเป็นโอเวอร์โหลด .. ฉันหวังว่าฉันจะนึกถึงข้อความง่ายๆสำหรับเรื่องนี้
Eugene

13

วิธีการคงที่ไม่สามารถลบล้างได้

นี่ไม่เป็นความจริงอย่างแน่นอน โค้ดตัวอย่างหมายความว่าเมธอด ts ใน B ซ่อนเมธอด ts ใน A ดังนั้นมันจึงไม่ถูกแทนที่อย่างแน่นอน บนJavaranchมีคำอธิบายที่ดี


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

1
ขออภัยลิงก์ของคุณใช้งานไม่ได้อีกต่อไป เป็นไปได้ไหมที่จะแก้ไขปัญหานี้
Mathias Bader

การเชื่อมโยง JavaRanch ไม่ทำงาน แต่ Googling คำสำคัญที่เปิดขึ้นที่ลิงค์นี้รหัสไร่
Sundeep

ฉันได้แก้ไขโพสต์โดยแทนที่ลิงก์ที่ตายแล้วโดยลิงก์ที่โพสต์โดย Sundeep
MC Emperor

10

วิธีการคงที่เป็นของคลาสไม่ใช่อินสแตนซ์

A.ts()และB.ts()มักจะเป็นวิธีการที่แยกจากกัน

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

คุณคิดว่าข้อความแสดงข้อผิดพลาดจะใช้คำที่ซ่อนอยู่แทนที่จะแทนที่ ...


6

คุณอาจพบว่าตัวเองอยู่ในฐานะที่จะคิดที่จะทำให้วิธีการคงที่เป็นขั้นสุดท้ายโดยพิจารณาสิ่งต่อไปนี้:

มีคลาสต่อไปนี้:

class A {
    static void ts() {
        System.out.print("A");
    }
}
class B extends A {
    static void ts() {
        System.out.print("B");
    }
}

ตอนนี้วิธีที่ 'ถูกต้อง' ในการเรียกวิธีการเหล่านี้จะเป็น

A.ts();
B.ts();

ซึ่งจะส่งผลให้ABแต่คุณสามารถเรียกใช้เมธอดบนอินสแตนซ์ได้:

A a = new A();
a.ts();
B b = new B();
b.ts();

ซึ่งจะส่งผลABเช่นกัน

พิจารณาสิ่งต่อไปนี้:

A a = new B();
a.ts();

Aที่จะพิมพ์ Bที่อาจทำให้คุณประหลาดใจตั้งแต่คุณเป็นจริงมีวัตถุของคลาส แต่เนื่องจากคุณกำลังเรียกมันจากการอ้างอิงจากประเภทนี้ก็จะเรียกA A.ts()คุณสามารถพิมพ์Bโดยใช้รหัสต่อไปนี้:

A a = new B();
((B)a).ts();

Bในทั้งสองกรณีวัตถุที่คุณมีความเป็นจริงจากชั้นเรียน แต่ขึ้นอยู่กับตัวชี้ที่ชี้ไปที่วัตถุคุณจะเรียกใช้วิธีการจากAหรือจากBหรือจาก

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

B b = new B();
b.ts();

โอเคยอมรับว่าสร้างขึ้นอย่างใด แต่อาจสมเหตุสมผลสำหรับบางกรณี

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


0

วิธีการ ts () ใน B ไม่ได้แทนที่วิธีการ ts () ใน A แต่เป็นอีกวิธีหนึ่ง คลาส B ไม่เห็นวิธีการ ts () ใน A เนื่องจากเป็นแบบคงที่ดังนั้นจึงสามารถประกาศเมธอดของตัวเองที่เรียกว่า ts ()

อย่างไรก็ตามหากเมธอดนั้นเป็นที่สิ้นสุดคอมไพเลอร์จะรับว่ามีเมธอด ts () ใน A ที่ไม่ควรถูกแทนที่ใน B


ฉันไม่คิดว่าสิ่งนี้จะอธิบายได้ว่าทำไม 'ขั้นสุดท้าย' จึงหมายความว่าวิธีการเหล่านี้ไม่สามารถอยู่ร่วมกันได้ อย่างที่คุณพูดหากไม่มี "ขั้นสุดท้าย" ก็ไม่มีปัญหา คุณบอกว่ามันไม่ได้ลบล้าง แต่ก็บอกว่าปัญหาคือ B ไม่สามารถแทนที่เมธอดของ A ได้
Sean Owen

แน่นอนว่าเป็นเช่นนั้นฉันระบุว่า B ไม่เห็นเมธอด ts () ใน A (มัน 'ซ่อน') แต่ตัวปรับแต่งขั้นสุดท้ายไม่ 'ซ่อน' วิธีการจากคลาสที่ขยายอีกวิธีหนึ่ง แต่เอ๊ะตกลง
amischiefr

0

ฉันคิดว่าข้อผิดพลาดในการรวบรวมค่อนข้างทำให้เข้าใจผิดที่นี่ ไม่ควรพูดว่า "overridden method is static final." แต่ควรกล่าวว่า "overridden method is final" ตัวปรับค่าคงที่ไม่เกี่ยวข้องที่นี่


1
คุณลองพิจารณาวิธีการคงที่ใน B แทนที่หนึ่งใน A?
Koray Tugay

@KorayTugay ฉันแค่สงสัยว่าคอมไพเลอร์จะมองไปที่วิธีการที่อาจเขียนทับได้เป็นครั้งแรกหรือไม่ (ละเว้นคงที่ชั่วขณะ) เห็นขั้นสุดท้ายเมื่อล้มเหลว เพียงแค่เดาอย่างดุเดือด
ยูจีน

Balus ฉันคิดว่านี่เป็นคำตอบคุณภาพต่ำเพียงคำตอบเดียวที่คุณมีใน StackOverflow เมื่อพิจารณาถึงคำตอบที่ยอดเยี่ยมทั้งหมดของคุณคำตอบนี้ไม่ได้เป็นของพวกเขา @BalusC
Koray Tugay

@KorayTugay: ตอนนั้นฉันไม่มีชื่อเสียงพอที่จะแสดงความคิดเห็น :) ถ้าคุณตอบกลับฉันจะลบคำตอบไม่มีปัญหา
BalusC

0

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

class Writer { 
    public static void doo(){
        System.out.println("sth");
    } 
}
class Author extends Writer{ 
    public void doo(){
        System.out.println("ok"); // error overridden method is static
    }
}

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

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