เป็นไปได้ไหมที่จะสร้างคลาสภายในแบบไม่ระบุชื่อใน Java Static


123

ใน Java คลาสที่ซ้อนกันสามารถเป็นstaticหรือไม่ก็ได้ หากเป็นstaticเช่นนั้นจะไม่มีการอ้างอิงถึงตัวชี้ของอินสแตนซ์ที่มีอยู่ (ยังไม่เรียกว่าคลาสภายในอีกต่อไปเรียกว่าคลาสที่ซ้อนกัน)

การลืมสร้างคลาสที่ซ้อนกันstaticเมื่อไม่ต้องการการอ้างอิงนั้นอาจทำให้เกิดปัญหากับการรวบรวมขยะหรือการวิเคราะห์การหลบหนี

เป็นไปได้ไหมที่จะสร้างคลาสภายในที่ไม่ระบุตัวตนstaticด้วย หรือคอมไพเลอร์คิดออกโดยอัตโนมัติ (ซึ่งทำได้เพราะไม่มีคลาสย่อยใด ๆ )?

ตัวอย่างเช่นหากฉันทำการเปรียบเทียบแบบไม่ระบุตัวตนฉันแทบไม่ต้องการการอ้างอิงถึงภายนอกเลย:

  Collections.sort(list, new Comparator<String>(){
       int compare(String a, String b){
          return a.toUpperCase().compareTo(b.toUpperCase());
       }
  }

"การรวบรวมขยะหรือการวิเคราะห์การหลบหนี" มีปัญหาอย่างไรเมื่อลืมทำให้ชั้นในคงที่ ฉันคิดว่านี่เป็นเรื่องของการแสดงเท่านั้น ...
Tim Büthe

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

ฉันรู้ว่านี่เป็นเพียงตัวอย่าง แต่เนื่องจากเป็นรายการที่เกิดซ้ำจึงควรกล่าวถึงว่าใช้Collections.sort(list, String.CASE_INSENSITIVE_ORDER)งานได้ตั้งแต่ Java 2 อ่านเนื่องจากมี Collection API อยู่ ...
Holger

คำตอบ:


138

ไม่คุณทำไม่ได้และไม่คอมไพเลอร์ไม่สามารถคิดออกได้ นี่คือเหตุผลที่ FindBugs แนะนำให้เปลี่ยนคลาสภายในที่ไม่ระบุชื่อเป็นคลาสที่staticซ้อนกันอยู่เสมอหากไม่ใช้thisการอ้างอิงโดยนัย

แก้ไข:ทอม Hawtin - tackline บอกว่าถ้าชั้นไม่ระบุชื่อจะถูกสร้างขึ้นในบริบทแบบคงที่ (เช่นในmainวิธีการ), staticชั้นที่ไม่ระบุชื่อในความเป็นจริง แต่ JLS ไม่เห็นด้วย :

คลาสที่ไม่ระบุชื่อไม่เคยเป็นabstract(§8.1.1.1) คลาสที่ไม่ระบุชื่อเป็นคลาสภายในเสมอ (§8.1.3); มันไม่เคยstatic(§8.1.1, §8.5.1) คลาสที่ไม่ระบุชื่อมักจะโดยปริยายเสมอfinal(§8.1.1.2)

อภิธานศัพท์ Java ของ Roedy Green กล่าวว่าความจริงที่ว่าคลาสที่ไม่ระบุชื่อได้รับอนุญาตในบริบทคงที่นั้นขึ้นอยู่กับการนำไปใช้งาน:

หากคุณต้องการทำให้ผู้ที่ดูแลรหัสของคุณยุ่งเหยิง wags ที่ค้นพบjavac.exeจะอนุญาตให้คลาสที่ไม่ระบุชื่อภายในstaticโค้ดและstaticวิธีการเริ่มต้นแม้ว่าข้อมูลจำเพาะของภาษาจะระบุว่าไม่ใช่คลาสที่ไม่ระบุชื่อstaticก็ตาม แน่นอนว่าคลาสที่ไม่ระบุตัวตนเหล่านี้ไม่มีสิทธิ์เข้าถึงฟิลด์อินสแตนซ์ของวัตถุ ฉันไม่แนะนำให้ทำเช่นนี้ คุณลักษณะอาจจะดึงในเวลาใดก็ได้

แก้ไข 2: JLS ครอบคลุมบริบทคงที่ชัดเจนมากขึ้นใน§15.9.2 :

ให้Cเป็นคลาสที่กำลังสร้างอินสแตนซ์และให้ฉันเป็นอินสแตนซ์ที่กำลังสร้างขึ้น ถ้าCเป็นคลาสภายในฉันอาจมีอินสแตนซ์ปิดล้อมทันที อินสแตนซ์ที่ล้อมรอบทันทีของi (§8.1.3) ถูกกำหนดดังนี้

  • ถ้าCเป็นคลาสที่ไม่ระบุชื่อแล้ว:
    • ถ้านิพจน์การสร้างอินสแตนซ์คลาสเกิดขึ้นในบริบทแบบคงที่ (§8.1.3) แสดงว่าฉันไม่มีอินสแตนซ์ที่ปิดล้อมทันที
    • มิฉะนั้นอินสแตนซ์ทันทีการปิดล้อมของฉันthisคือ

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


19
+1 สำหรับ FindBugs - นักพัฒนา Java ทุกคนควรมีสิ่งนี้ในบิลด์
Andrew Duffy

13
นั่นเป็นเรื่องที่โชคร้ายมากเพราะนั่นหมายความว่าคุณอาจต้องการหลีกเลี่ยงไวยากรณ์ที่ค่อนข้างรัดกุมเนื่องจากเหตุผลด้านประสิทธิภาพ
Thilo

2
JLS 3rd Ed เกี่ยวข้องกับกรณีของคลาสภายในในบริบทคงที่ พวกเขาไม่คงที่ในความหมายของ JLS แต่เป็นแบบคงที่ในความหมายที่ให้ไว้ในคำถาม
Tom Hawtin - แท็กไลน์

6
นี่คือตัวอย่างของการใช้งานที่ขึ้นอยู่กับ: โค้ดนี้พิมพ์trueโดยใช้ javac (sun-jdk-1.7.0_10) และfalseใช้คอมไพเลอร์ Eclipse
Paul Bellora

1
@MichaelMyers ฉันได้พยายามจำลอง FindBugs แจ้งเตือนฉันถึงการทำ Anonymous Inner โดยไม่ใช้การอ้างอิง 'this' และไม่มีอะไรเกิดขึ้น คุณสามารถสาธิตวิธีที่ FindBugs แจ้งเตือนคุณตามที่คุณกล่าวไว้ในตอนต้นของคำตอบได้หรือไม่? เพียงแค่วางลิงก์หรือสิ่งที่เคย
Thufir Hawat

15

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

มีความแตกต่างทางเทคนิคบางประการระหว่างคลาสภายในในบริบทคงที่และคลาสที่ซ้อนกันแบบคงที่ หากคุณสนใจอ่าน JLS 3rd Ed


ที่จริงฉันเอาคืน JLS ไม่เห็นด้วย java.sun.com/docs/books/jls/third%5Fedition/html/… : "คลาสที่ไม่ระบุตัวตนมักจะเป็นคลาสภายในเสมอมันจะไม่คงที่"
Michael Myers

1
คงที่ในความหมายที่แตกต่างจากคำถามนั้น
Tom Hawtin - แท็กไลน์

1
ฉันได้เพิ่มคำชี้แจงเล็กน้อย
Tom Hawtin - แท็กไลน์

15

ฉันคิดว่ามีความสับสนเล็กน้อยในระบบการตั้งชื่อที่นี่ซึ่งยอมรับว่างี่เง่าและสับสนเกินไป

ไม่ว่าคุณจะเรียกมันว่าอะไรรูปแบบเหล่านี้ (และรูปแบบบางส่วนที่มีการมองเห็นที่แตกต่างกัน) ล้วนเป็นไปได้ Java ปกติและถูกกฎหมาย:

public class MyClass {
  class MyClassInside {
  }
}

public class MyClass {
  public static class MyClassInside {
  }
}

public class MyClass {
  public void method() {
    JComponent jc = new JComponent() {
      ...
    }
  }
}

public class MyClass {
  public static void myStaticMethod() {
    JComponent jc = new JComponent() {
      ...
    }
  }
}

พวกเขารองรับในข้อกำหนดภาษา (ถ้าคุณกังวลจริงๆโปรดดูหัวข้อ 15.9.5.1 สำหรับหนึ่งในวิธีการคงที่)

แต่คำพูดนี้ผิดธรรมดา :

javac.exe จะอนุญาตคลาสที่ไม่ระบุชื่อภายในรหัสเริ่มต้นแบบคงที่และวิธีการแบบคงที่แม้ว่าข้อมูลจำเพาะของภาษาจะระบุว่าคลาสที่ไม่ระบุชื่อจะไม่คงที่

ผมคิดว่าผู้เขียนยกมาเป็นความสับสนคงคำหลักที่มีคงบริบท (เป็นที่ยอมรับ JLS ก็ค่อนข้างสับสนในแง่นี้เช่นกัน)

ตามจริงแล้วรูปแบบทั้งหมดข้างต้นนั้นใช้ได้ดี (ไม่ว่าคุณจะเรียกมันว่า "ซ้อน" "ภายใน", "ไม่ระบุตัวตน" ก็ตาม ... ) จริงๆแล้วไม่มีใครลบฟังก์ชันนี้ออกไปใน Java รุ่นถัดไปอย่างกะทันหัน สุจริต!


2
"(ยอมรับว่า JLS ก็ค่อนข้างสับสนในแง่นี้เหมือนกัน)" คุณเข้าใจถูกแล้ว มันฟังดูแปลก ๆ ที่บอกว่ามันขึ้นอยู่กับการนำไปใช้ แต่ฉันจำไม่ได้ว่าเคยเห็นข้อผิดพลาดที่ชัดเจนในอภิธานศัพท์ Java มาก่อน จากนี้ไปฉันจะใช้เกลือเม็ดหนึ่ง
Michael Myers

2
จริงๆแล้วเราไม่ได้พูดถึงรูปแบบใด ๆ เราหมายความว่าคลาสที่ซ้อนกันแบบไม่ระบุชื่อเป็นแบบคงที่ เช่นเพิ่ม "คงที่" ระหว่างnewและJComponentในตัวอย่างที่สามของคุณ
Timmmm

ฉันเพิ่มคำชี้แจงให้กับคำถามเดิมเพื่อแสดงสิ่งที่ต้องการ
Timmmm

@MichaelMyers คำสั่งใน JLS จำเป็นต้องตีความเสมอ
Pacerier

6

คลาสภายในไม่สามารถคงที่ได้ - คลาสที่ซ้อนกันแบบคงที่ไม่ใช่คลาสภายใน ชวาเจรจากวดวิชาเกี่ยวกับเรื่องนี้ที่นี่


1
ฉันได้อัปเดตคำถามโดยอ้างอิงถึงระบบการตั้งชื่ออย่างเป็นทางการ
Thilo

0

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


-3

เกี่ยวกับการสร้างคลาสภายในแบบไม่ระบุชื่อแบบคงที่โดยการเรียกใช้ภายในวิธีการแบบคงที่

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


5
-1: การสร้างคลาสที่ไม่ระบุตัวตนภายในวิธีการแบบคงที่จะลบการอ้างอิงไปยังคลาสภายนอก คุณสามารถทดสอบสิ่งนี้ได้โดยพยายามจัดลำดับคลาสที่ไม่ระบุชื่อและไม่ทำให้คลาสที่ปิดล้อมสามารถต่ออนุกรมกันได้ (ฉันเพิ่งทำไป)
Christian Semrau
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.