คุณจะค้นหาคลาสย่อยทั้งหมดของคลาสที่กำหนดใน Java ได้อย่างไร?


207

มีวิธีการอย่างไรและพยายามค้นหาคลาสย่อยทั้งหมดของคลาสที่กำหนด (หรือผู้พัฒนาระบบทั้งหมดของอินเตอร์เฟสที่กำหนด) ใน Java? ณ ตอนนี้ฉันมีวิธีการทำเช่นนี้ แต่ฉันพบว่ามันค่อนข้างไม่มีประสิทธิภาพ (ที่จะพูดน้อย) วิธีการคือ:

  1. รับรายการชื่อคลาสทั้งหมดที่มีอยู่บนเส้นทางคลาส
  2. โหลดแต่ละคลาสและทดสอบเพื่อดูว่าเป็นคลาสย่อยหรือตัวดำเนินการของคลาสหรืออินเตอร์เฟสที่ต้องการ

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


1
แม้ว่าวิธีการแก้ปัญหาโดยใช้การสะท้อนแสงและฤดูใบไม้ผลิดูน่าสนใจ แต่ฉันต้องการวิธีแก้ปัญหาง่ายๆที่ไม่ได้พึ่งพา ดูเหมือนว่ารหัสต้นฉบับของฉัน (มีการปรับแต่ง) เป็นวิธีที่จะไป
Avrom

1
แน่นอนคุณสามารถใช้วิธี getSupeClass ซ้ำ?

ฉันกำลังมองหาคลาสย่อยทั้งหมดของคลาสที่ระบุโดยเฉพาะ getSuperClass จะไม่บอกคุณว่าคลาสย่อยมีคลาสใดเพียงรับคลาสซุปเปอร์ทันทีสำหรับคลาสย่อยที่ระบุ นอกจากนี้เมธอด isAssignableFrom บน Class จะเหมาะสมกว่าสำหรับสิ่งที่คุณแนะนำ (ไม่จำเป็นต้องเรียกซ้ำ)
Avrom

คำถามนี้เชื่อมโยงจากรายการซ้ำหลายรายการ แต่ไม่มีคำตอบ Java ที่ธรรมดาและมีประโยชน์ เฮ้อ ...
Eric Duminil

คำตอบ:


77

ไม่มีวิธีอื่นที่จะทำนอกเหนือจากสิ่งที่คุณอธิบาย ลองคิดดูสิ - ใครจะรู้ได้ว่าคลาสใดที่ขยาย ClassX ได้โดยไม่ต้องสแกนแต่ละคลาสใน classpath

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


23
ขณะนี้มีไลบรารีแบบง่าย ๆ ที่เรียกว่าorg.reflectionsซึ่งช่วยงานนี้และงานสะท้อนทั่วไปอื่น ๆ ด้วยห้องสมุดนี้คุณสามารถโทรreflections.getSubTypesOf(aClazz)) ลิงค์ได้
Enwired

@matt b - หากต้องสแกนคลาสทั้งหมดหมายความว่ามีการลดประสิทธิภาพเมื่อคุณมีคลาสจำนวนมากในโครงการของคุณแม้ว่าจะมีคลาสย่อยเพียงไม่กี่คลาสเท่านั้นหรือไม่
LeTex

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

คำตอบโดย fforw ทำงานสำหรับฉันและควรถูกทำเครื่องหมายเป็นคำตอบที่ถูกต้อง ชัดเจนว่าเป็นไปได้ด้วยการสแกน classpath
Farrukh Najmi

คุณผิดลองดูคำตอบด้านล่างเกี่ยวกับไลบรารี่ Burningwave

127

การสแกนชั้นเรียนไม่ใช่เรื่องง่ายด้วยจาวาบริสุทธิ์

Spring Framework เสนอคลาสที่เรียกว่าClassPathScanningCandidateComponentProviderซึ่งสามารถทำสิ่งที่คุณต้องการได้ ตัวอย่างต่อไปนี้จะค้นหาคลาสย่อยทั้งหมดของ MyClass ในแพ็คเกจ org.example.package

ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
provider.addIncludeFilter(new AssignableTypeFilter(MyClass.class));

// scan in org.example.package
Set<BeanDefinition> components = provider.findCandidateComponents("org/example/package");
for (BeanDefinition component : components)
{
    Class cls = Class.forName(component.getBeanClassName());
    // use class cls found
}

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


23
False ควรส่งผ่านเป็นพารามิเตอร์เมื่อสร้าง ClassPathScanningCandidateComponentProvider เพื่อปิดการใช้งานตัวกรองเริ่มต้น ตัวกรองเริ่มต้นจะตรงกับคลาสประเภทอื่นเช่นสิ่งที่มีคำอธิบายประกอบกับ @Component เราต้องการให้ AssignableTypeFilter ทำงานที่นี่เท่านั้น
MCDS

คุณบอกว่ามันไม่ใช่เรื่องง่าย แต่เราจะทำอย่างไรถ้าเราต้องการที่จะทำกับจาวาบริสุทธิ์
Aequitas

49

ไม่สามารถทำได้โดยใช้เฉพาะ Java Reflections API ในตัว

มีโครงการที่ทำการสแกนและทำดัชนีที่เป็น classpath ของคุณเพื่อให้คุณสามารถเข้าถึงข้อมูลนี้ ...

สะท้อน

การวิเคราะห์ข้อมูลเมตารันไทม์ Java ในจิตวิญญาณของ Scannotations

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

ใช้ Reflections คุณสามารถสอบถามข้อมูลเมตาของคุณสำหรับ:

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

(ข้อจำกัดความรับผิดชอบ: ฉันไม่ได้ใช้ แต่คำอธิบายของโครงการดูเหมือนจะเหมาะสมกับความต้องการของคุณ)


1
น่าสนใจ โครงการดูเหมือนจะมีการพึ่งพาบางอย่างซึ่งเอกสารของพวกเขาดูเหมือนจะไม่พูดถึง คือ (สิ่งที่ฉันพบจนถึง): javaassist, log4J, XStream
Avrom

3
ฉันรวม projekt นี้ด้วย maven และมันใช้ได้ดี การรับ subclasses เป็นตัวอย่างซอร์สโค้ดแรกและมีความยาวสองบรรทัด :-)
KarlsFriend

เป็นไปไม่ได้ที่จะใช้เฉพาะ Java Reflections API ในตัวหรือไม่สะดวกอย่างนั้นหรือ
Flow

1
โปรดระวังเมื่อคุณจะใช้ Reflections จากนั้นปรับใช้แอพของคุณ WAR เพื่อ GlassFish! มีข้อขัดแย้งในไลบรารีของ Guava และการปรับใช้จะล้มเหลวเนื่องจากข้อผิดพลาดในการปรับใช้ CDI ล้มเหลว: WELD-001408 - โปรดดูGLASSFISH-20579สำหรับรายละเอียดเพิ่มเติม FastClasspathScannerเป็นวิธีแก้ปัญหาในกรณีนี้
lu_ko

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

10

อย่าลืมว่าJavadoc ที่สร้างขึ้นสำหรับคลาสจะมีรายการของคลาสย่อยที่รู้จัก (และสำหรับอินเตอร์เฟส


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

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

และคุณอาจพลาดบางคลาสไม่ว่าในกรณีใด: ฉันสามารถโหลด jar ใหม่เข้าไปใน classpath (ระหว่างรันไทม์) และการตรวจจับทุกอย่างที่เกิดขึ้นก่อนหน้านี้จะล้มเหลว
คำถามที่

10

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


9

ฉันทำสิ่งนี้เมื่อหลายปีก่อน วิธีที่น่าเชื่อถือที่สุดในการทำเช่นนี้ (เช่นกับ Java APIs อย่างเป็นทางการและไม่มีการพึ่งพาภายนอก) คือการเขียน doclet แบบกำหนดเองเพื่อสร้างรายการที่สามารถอ่านได้ที่รันไทม์

คุณสามารถเรียกใช้จากบรรทัดคำสั่งดังนี้:

javadoc -d build -doclet com.example.ObjectListDoclet -sourcepath java/src -subpackages com.example

หรือเรียกใช้จากมดเช่นนี้

<javadoc sourcepath="${src}" packagenames="*" >
  <doclet name="com.example.ObjectListDoclet" path="${build}"/>
</javadoc>

นี่คือรหัสพื้นฐาน:

public final class ObjectListDoclet {
    public static final String TOP_CLASS_NAME =  "com.example.MyClass";        

    /** Doclet entry point. */
    public static boolean start(RootDoc root) throws Exception {
        try {
            ClassDoc topClassDoc = root.classNamed(TOP_CLASS_NAME);
            for (ClassDoc classDoc : root.classes()) {
                if (classDoc.subclassOf(topClassDoc)) {
                    System.out.println(classDoc);
                }
            }
            return true;
        }
        catch (Exception ex) {
            ex.printStackTrace();
            return false;
        }
    }
}

เพื่อความง่ายฉันได้ลบอาร์กิวเมนต์บรรทัดคำสั่งและฉันกำลังเขียนไปที่ System.out ไม่ใช่ไฟล์


ในขณะที่ใช้โปรแกรมนี้อาจเป็นเรื่องยุ่งยากฉันต้องบอกว่า - วิธีการอันชาญฉลาด !
Janaka Bandara

8

ฉันรู้ว่าฉันไปปาร์ตี้นี้ไม่กี่ปีที่ผ่านมา แต่ฉันเจอคำถามนี้พยายามแก้ปัญหาเดียวกัน คุณสามารถใช้การค้นหาภายในของ Eclipse โดยทางโปรแกรมหากคุณกำลังเขียนปลั๊กอิน Eclipse (และใช้ประโยชน์จากแคชของพวกเขา ฯลฯ ) เพื่อค้นหาคลาสที่ใช้อินเตอร์เฟซ นี่คือการตัดครั้งแรกของฉัน (หยาบมาก):

  protected void listImplementingClasses( String iface ) throws CoreException
  {
    final IJavaProject project = <get your project here>;
    try
    {
      final IType ifaceType = project.findType( iface );
      final SearchPattern ifacePattern = SearchPattern.createPattern( ifaceType, IJavaSearchConstants.IMPLEMENTORS );
      final IJavaSearchScope scope = SearchEngine.createWorkspaceScope();
      final SearchEngine searchEngine = new SearchEngine();
      final LinkedList<SearchMatch> results = new LinkedList<SearchMatch>();
      searchEngine.search( ifacePattern, 
      new SearchParticipant[]{ SearchEngine.getDefaultSearchParticipant() }, scope, new SearchRequestor() {

        @Override
        public void acceptSearchMatch( SearchMatch match ) throws CoreException
        {
          results.add( match );
        }

      }, new IProgressMonitor() {

        @Override
        public void beginTask( String name, int totalWork )
        {
        }

        @Override
        public void done()
        {
          System.out.println( results );
        }

        @Override
        public void internalWorked( double work )
        {
        }

        @Override
        public boolean isCanceled()
        {
          return false;
        }

        @Override
        public void setCanceled( boolean value )
        {
        }

        @Override
        public void setTaskName( String name )
        {
        }

        @Override
        public void subTask( String name )
        {
        }

        @Override
        public void worked( int work )
        {
        }

      });

    } catch( JavaModelException e )
    {
      e.printStackTrace();
    }
  }

ปัญหาแรกที่ฉันเห็นคือฉันแค่เรียนเฉพาะที่ใช้อินเตอร์เฟซโดยตรงไม่ใช่คลาสย่อยทั้งหมดของพวกเขา - แต่การเรียกซ้ำเล็กน้อยไม่เคยทำร้ายใครเลย


3
หรือปรากฎว่าคุณไม่จำเป็นต้องค้นหาด้วยตัวเอง คุณสามารถรับ ITypeHierarchy โดยตรงจาก IType ของคุณโดยโทร. newTypeHierarchy () ที่: dev.eclipse.org/newslists/news.eclipse.tools.jdt/msg05036.html
Curtis

7

คำนึงถึงข้อ จำกัด ที่กล่าวถึงในคำตอบอื่น ๆ คุณยังสามารถใช้openpojo'sPojoClassFactory ( มีให้ใน Maven ) ในลักษณะดังต่อไปนี้:

for(PojoClass pojoClass : PojoClassFactory.enumerateClassesByExtendingType(packageRoot, Superclass.class, null)) {
    System.out.println(pojoClass.getClazz());
}

ที่ไหนpackageRootเป็น String รากของแพคเกจที่คุณต้องการค้นหาใน (เช่น"com.mycompany"หรือแม้เพียงแค่"com") และSuperclassเป็น supertype คุณ (งานนี้ในการเชื่อมต่อเช่นกัน)


โดยวิธีการแก้ปัญหาที่เร็วที่สุดและสง่างามที่สุดจากสิ่งที่แนะนำ
KidCrippler

4

ฉันเพิ่งเขียนตัวอย่างง่าย ๆ เพื่อใช้ในorg.reflections.Reflectionsการรับ subclasses ของคลาสนามธรรม:

https://github.com/xmeng1/ReflectionsDemo


มันจะมีประสิทธิผลมากขึ้นถ้าคุณโพสต์ตัวอย่างโค้ดที่นี่แทนที่จะลิงค์กับคลาสจำนวนมากที่จะขุด
JesseBoyd

4

ขึ้นอยู่กับข้อกำหนดเฉพาะของคุณในบางกรณีกลไกบริการตัวโหลดของ Java อาจบรรลุสิ่งที่คุณต้องการ

ในระยะสั้นจะช่วยให้นักพัฒนาประกาศอย่างชัดเจนว่าคลาสย่อยคลาสอื่น ๆ บางคลาส (หรือใช้อินเตอร์เฟซบางส่วน) โดยการแสดงรายการในไฟล์ในMETA-INF/servicesไดเรกทอรีของไฟล์ JAR / WAR จากนั้นจะสามารถค้นพบได้โดยใช้java.util.ServiceLoaderคลาสซึ่งเมื่อได้รับClassวัตถุจะสร้างอินสแตนซ์ของคลาสย่อยที่ประกาศทั้งหมดของคลาสนั้น (หรือหากเป็นClassตัวแทนของอินเตอร์เฟสคลาสทั้งหมดที่ใช้อินเตอร์เฟสนั้น)

ข้อได้เปรียบหลักของวิธีนี้คือไม่จำเป็นต้องสแกนคลาสพา ธ ทั้งหมดสำหรับคลาสย่อยด้วยตนเอง - ตรรกะการค้นพบทั้งหมดจะอยู่ภายในServiceLoaderคลาสและโหลดคลาสที่ประกาศอย่างชัดเจนในMETA-INF/servicesไดเรกทอรีเท่านั้น (ไม่ใช่ทุกคลาสบนคลาสพา ธ ) .

อย่างไรก็ตามมีข้อเสียบางประการ:

  • มันจะไม่พบคลาสย่อยทั้งหมดเท่านั้นที่จะมีการประกาศอย่างชัดเจน ดังนั้นหากคุณต้องการค้นหาคลาสย่อยทั้งหมดวิธีนี้อาจไม่เพียงพอ
  • มันต้องการนักพัฒนาเพื่อประกาศคลาสอย่างชัดเจนภายใต้META-INF/servicesไดเรกทอรี นี่เป็นภาระเพิ่มเติมสำหรับนักพัฒนาและอาจเกิดข้อผิดพลาดได้ง่าย
  • ServiceLoader.iterator()สร้างอินสแตนซ์ subclass, ไม่ได้เป็นของClassวัตถุ ทำให้สองประเด็น:
    • คุณไม่ได้รับการพูดใด ๆ เกี่ยวกับวิธีการสร้างคลาสย่อย - คอนสตรัคเตอร์ no-arg ถูกใช้เพื่อสร้างอินสแตนซ์
    • ดังนั้นคลาสย่อยต้องมี Constructor เริ่มต้นหรือต้องอธิบายความชัดเจนของ Constructor ที่ไม่มีอาร์กิวเมนต์

เห็นได้ชัดว่า Java 9 จะกล่าวถึงข้อบกพร่องเหล่านี้ (โดยเฉพาะอย่างยิ่งเรื่องที่เกี่ยวข้องกับการสร้างอินสแตนซ์ของคลาสย่อย)

ตัวอย่าง

สมมติว่าคุณสนใจหาคลาสที่ใช้อินเทอร์เฟซcom.example.Example:

package com.example;

public interface Example {
    public String getStr();
}

คลาสcom.example.ExampleImplใช้อินเตอร์เฟสนั้น:

package com.example;

public class ExampleImpl implements Example {
    public String getStr() {
        return "ExampleImpl's string.";
    }
}

คุณจะประกาศชั้นเรียนExampleImplคือการดำเนินการExampleโดยการสร้างไฟล์ที่มีข้อความMETA-INF/services/com.example.Examplecom.example.ExampleImpl

จากนั้นคุณสามารถขอรับอินสแตนซ์ของการติดตั้งแต่ละครั้งของExample(รวมถึงอินสแตนซ์ของExampleImpl) ดังนี้:

ServiceLoader<Example> loader = ServiceLoader.load(Example.class)
for (Example example : loader) {
    System.out.println(example.getStr());
}

// Prints "ExampleImpl's string.", plus whatever is returned
// by other declared implementations of com.example.Example.

3

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

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


3

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


3

ฉันใช้ lib การสะท้อนซึ่งสแกน classpath ของคุณสำหรับคลาสย่อยทั้งหมด: https://github.com/ronmamo/reflections

นี่คือวิธีที่จะทำ:

Reflections reflections = new Reflections("my.project");
Set<Class<? extends SomeType>> subTypes = reflections.getSubTypesOf(SomeType.class);

2

เพิ่มลงในแผนที่แบบคงที่ภายใน (this.getClass (). getName ()) ผู้สร้างคลาสผู้ปกครอง (หรือสร้างหนึ่งเริ่มต้น) แต่สิ่งนี้จะได้รับการปรับปรุงในรันไทม์ หากการกำหนดค่าเริ่มต้นขี้เกียจเป็นตัวเลือกคุณสามารถลองวิธีนี้


1

คุณสามารถใช้ไลบรารี org.reflections จากนั้นสร้างวัตถุของคลาส Reflections ใช้วัตถุนี้คุณจะได้รับรายชื่อของคลาสย่อยทั้งหมดของชั้นที่กำหนด https://www.javadoc.io/doc/org.reflections/reflections/0.9.10/org/reflections/Reflections.html

    Reflections reflections = new Reflections("my.project.prefix");
    System.out.println(reflections.getSubTypesOf(A.class)));

0

ฉันต้องทำสิ่งนี้เป็นกรณีทดสอบเพื่อดูว่ามีการเพิ่มคลาสใหม่ในรหัสหรือไม่ นี่คือสิ่งที่ฉันทำ

final static File rootFolder = new File(SuperClass.class.getProtectionDomain().getCodeSource().getLocation().getPath());
private static ArrayList<String> files = new ArrayList<String>();
listFilesForFolder(rootFolder); 

@Test(timeout = 1000)
public void testNumberOfSubclasses(){
    ArrayList<String> listSubclasses = new ArrayList<>(files);
    listSubclasses.removeIf(s -> !s.contains("Superclass.class"));
    for(String subclass : listSubclasses){
        System.out.println(subclass);
    }
    assertTrue("You did not create a new subclass!", listSubclasses.size() >1);     
}

public static void listFilesForFolder(final File folder) {
    for (final File fileEntry : folder.listFiles()) {
        if (fileEntry.isDirectory()) {
            listFilesForFolder(fileEntry);
        } else {
            files.add(fileEntry.getName().toString());
        }
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.