रनटाइम के दौरान, जावा एप्लिकेशन में सभी कक्षाएं खोजें जो बेस क्लास का विस्तार करती हैं


147

मैं कुछ इस तरह करना चाहता हूं:

List<Animal> animals = new ArrayList<Animal>();

for( Class c: list_of_all_classes_available_to_my_app() )
   if (c is Animal)
      animals.add( new c() );

इसलिए, मैं अपने एप्लिकेशन के ब्रह्मांड के सभी वर्गों को देखना चाहता हूं, और जब मुझे कोई ऐसा व्यक्ति मिलता है जो एनिमल से उतरता है, तो मैं उस प्रकार का एक नया ऑब्जेक्ट बनाना चाहता हूं और इसे सूची में जोड़ना चाहता हूं। यह मुझे चीजों की सूची को अपडेट किए बिना कार्यक्षमता जोड़ने की अनुमति देता है। मैं निम्नलिखित से बच सकता हूं:

List<Animal> animals = new ArrayList<Animal>();
animals.add( new Dog() );
animals.add( new Cat() );
animals.add( new Donkey() );
...

उपरोक्त दृष्टिकोण के साथ, मैं बस एक नया वर्ग बना सकता हूं जो एनिमल का विस्तार करता है और इसे स्वचालित रूप से उठाया जाएगा।

अद्यतन: 10/16/2008 9:00 प्रशांत मानक समय:

इस सवाल ने बहुत सारी शानदार प्रतिक्रियाएँ पैदा की हैं - धन्यवाद। प्रतिक्रियाओं और मेरे शोध से, मैंने पाया है कि जो मैं वास्तव में करना चाहता हूं वह सिर्फ जावा के तहत संभव नहीं है। ऐसे दृष्टिकोण हैं, जैसे कि ddimitrov के ServiceLoader तंत्र जो काम कर सकते हैं - लेकिन वे जो मैं चाहते हैं उसके लिए बहुत भारी हैं, और मुझे विश्वास है कि मैं समस्या को जावा कोड से बाहरी कॉन्फ़िगरेशन फ़ाइल में स्थानांतरित करता हूं। अपडेट 5/10/19 (11 साल बाद!) अब कई पुस्तकालय हैं जो @ इवानिक के उत्तर के अनुसार इस में मदद कर सकते हैं । org.reflections अच्छा लग रहा है। इसके अलावा @Luke Hutchison के जवाब से ClassGraph दिलचस्प लग रहा है। साथ ही उत्तरों में कई और संभावनाएँ हैं।

यह बताने का एक और तरीका कि मैं क्या चाहता हूं: मेरे एनिमल क्लास में एक स्थिर फ़ंक्शन उन सभी वर्गों को ढूंढता है, जो किसी भी और कॉन्फ़िगरेशन / कोडिंग के बिना - एनिमल से विरासत में प्राप्त करता है। अगर मुझे कॉन्फ़िगर करना है, तो मैं उन्हें जानवरों की कक्षा में वैसे भी तुरंत रोक सकता हूं। मैं समझता हूं कि क्योंकि जावा प्रोग्राम सिर्फ .class फ़ाइलों का एक ढीला फेडरेशन है जो कि बस उसी तरह है।

दिलचस्प है, ऐसा लगता है कि यह सी # में काफी तुच्छ है।


1
थोड़ी देर खोज करने के बाद, ऐसा लगता है कि जावा में दरार करने के लिए यह एक कठिन अखरोट है। यहाँ एक थ्रेड है जिसमें कुछ जानकारी है: forum.sun.com/thread.jspa?threadID=341935&start=15 इसमें कार्यान्वयन मेरी आवश्यकता से अधिक हैं, मुझे लगता है कि मैं अभी के लिए दूसरे कार्यान्वयन के साथ रहना चाहूंगा
जॉनीलैम्बडा

एक उत्तर के रूप में रखें और पाठकों को उस पर वोट दें
VONC

3
C # में ऐसा करने का लिंक कुछ विशेष नहीं दिखाता है। AC # असेंबली जावा JAR फ़ाइल के बराबर है। यह संकलित वर्ग फ़ाइलों (और संसाधनों) का एक संग्रह है। लिंक दिखाता है कि कक्षाओं को एक विधानसभा से कैसे निकाला जाए। आप इसे लगभग आसानी से जावा के साथ कर सकते हैं। आपके लिए समस्या यह है कि आपको सभी JAR फ़ाइलों और सच्ची ढीली फ़ाइलों (निर्देशिका) के माध्यम से देखने की आवश्यकता है; आपको .NET के साथ भी ऐसा ही करने की आवश्यकता है (कुछ प्रकार या विभिन्न प्रकार के एक पथ को खोजें)।
केविन ब्रॉक

जवाबों:


156

मैं org.reflections का उपयोग करता हूं :

Reflections reflections = new Reflections("com.mycompany");    
Set<Class<? extends MyInterface>> classes = reflections.getSubTypesOf(MyInterface.class);

एक और उदाहरण:

public static void main(String[] args) throws IllegalAccessException, InstantiationException {
    Reflections reflections = new Reflections("java.util");
    Set<Class<? extends List>> classes = reflections.getSubTypesOf(java.util.List.class);
    for (Class<? extends List> aClass : classes) {
        System.out.println(aClass.getName());
        if(aClass == ArrayList.class) {
            List list = aClass.newInstance();
            list.add("test");
            System.out.println(list.getClass().getName() + ": " + list.size());
        }
    }
}

6
Org.reflections के लिंक के लिए धन्यवाद। मुझे गलती से लगा कि यह इतना आसान है जितना कि यह .Net दुनिया में है और इस जवाब ने मुझे बहुत समय बचाया है।
अक्माड

1
वास्तव में महान पैकेज "org.reflections" के लिए इस उपयोगी लिंक के लिए धन्यवाद! इसके साथ मैंने अपनी समस्या के लिए एक व्यावहारिक और साफ समाधान ढूंढ लिया है।
हार्टमुट पी।

3
क्या एक विशिष्ट पैकेज को ध्यान में रखते हुए, लेकिन सभी उपलब्ध वर्गों को लोड किए बिना सभी प्रतिबिंब प्राप्त करने का एक तरीका है?
जॉय बारूक

याद रखें कि कक्षाएं ढूंढने से उन्हें ( animals.add( new c() );आपके कोड की पंक्ति 5 ) को तुरंत हल करने की आपकी समस्या का समाधान नहीं होगा , क्योंकि Class.newInstance()मौजूद होने के दौरान , आपके पास कोई गारंटी नहीं है कि विशिष्ट वर्ग के पास एक पैरामीटर रहित रचनाकार है (C # आपको एक सामान्य में इसकी आवश्यकता है)
usr-local-

क्लासग्राफ भी देखें: github.com/classgraph/classgraph (अस्वीकरण, मैं लेखक हूं)। मैं एक अलग जवाब में एक कोड उदाहरण दूंगा।
ल्यूक हचिसन

34

जावा तरीका है कि आप जो चाहते हैं वह है ServiceLader तंत्र का उपयोग करना ।

इसके अलावा कई लोग एक अच्छी तरह से ज्ञात वर्ग स्थान (यानी /META-INF/services/myplugin.properties) में एक फ़ाइल होने और फिर सभी नामों से इस नाम के साथ सभी फ़ाइलों को मानने के लिए ClassLoader.getResources () का उपयोग करके अपना रोल करते हैं । यह प्रत्येक जार को अपने स्वयं के प्रदाताओं को निर्यात करने की अनुमति देता है और आप Class.forName () का उपयोग करके प्रतिबिंब द्वारा उन्हें त्वरित कर सकते हैं


1
आसानी से कक्षाओं पर एनोटेशन के आधार पर META-INF सेवाओं फ़ाइल पैदा करने के लिए, आप देख सकते हैं: metainf-services.kohsuke.org या code.google.com/p/spi
अग्ली

Bleah। बस एक जटिलता का स्तर जोड़ता है, लेकिन दोनों एक वर्ग बनाने की आवश्यकता को समाप्त नहीं करता है और फिर इसे दूर भूमि में पंजीकृत करता है।
केविन क्लाइन

कृपया 26
उत्तोलन के

4
वास्तव में, अगर आपको एक तरह के कामकाजी समाधान की आवश्यकता है, तो रिफ्लेक्शंस / स्कैनोटेशन एक अच्छा विकल्प है, फिर भी दोनों बाहरी निर्भरता को लागू करते हैं, गैर-निर्धारक हैं और जावा समुदाय प्रक्रिया द्वारा समर्थित नहीं हैं। इसके अलावा, मैं यह उजागर करना चाहता था कि आप कभी भी मज़बूती से सभी वर्गों को प्राप्त नहीं कर सकते हैं, क्योंकि यह किसी भी समय पतली हवा से एक नया निर्माण करने के लिए तुच्छ है। अपने सभी मौसा के लिए, जावा का सर्विस लोडर समझने और उपयोग करने में आसान है। मैं अलग-अलग कारणों से इससे दूर
रहूंगा

11

पहलू-उन्मुख दृष्टिकोण से इस बारे में सोचें; आप वास्तव में क्या करना चाहते हैं, रनटाइम के दौरान सभी वर्गों को पता है कि एचएवी ने एनिमल क्लास का विस्तार किया है। (मुझे लगता है कि यह आपकी शीर्षक से आपकी समस्या का थोड़ा अधिक सटीक वर्णन है; अन्यथा, मुझे नहीं लगता कि आपके पास कोई रनटाइम प्रश्न है)

इसलिए मुझे लगता है कि आप चाहते हैं कि आपके बेस क्लास (एनिमल) का एक कंस्ट्रक्टर बनाया जाए जो आपके स्टैटिक ऐरे में जुड़ता है (मैं एरेलेलिस्ट्स को पसंद करता हूं, खुद को, लेकिन हर एक को) वर्तमान क्लास का वह प्रकार जो इंस्टेंट किया जा रहा है।

तो, मोटे तौर पर;

public abstract class Animal
    {
    private static ArrayList<Class> instantiatedDerivedTypes;
    public Animal() {
        Class derivedClass = this.getClass();
        if (!instantiatedDerivedClass.contains(derivedClass)) {
            instantiatedDerivedClass.Add(derivedClass);
        }
    }

बेशक, आपको InstantiatedDerivedClass को इनिशियलाइज़ करने के लिए एनिमल पर एक स्टैटिक कंस्ट्रक्टर की ज़रूरत होगी ... मुझे लगता है कि यह वही करेगा जो आप शायद चाहते हैं। ध्यान दें कि यह निष्पादन-पथ पर निर्भर है; यदि आपके पास एक कुत्ता वर्ग है जो पशु से प्राप्त होता है जो कभी भी आह्वान नहीं करता है, तो आपके पास यह आपके पशु वर्ग की सूची में नहीं होगा।


6

दुर्भाग्य से यह पूरी तरह से संभव नहीं है क्योंकि क्लासलॉडर आपको यह नहीं बताएगा कि कौन सी कक्षाएं उपलब्ध हैं। हालाँकि, आप इस तरह से कुछ कर पाने के करीब पहुँच सकते हैं:

for (String classpathEntry : System.getProperty("java.class.path").split(System.getProperty("path.separator"))) {
    if (classpathEntry.endsWith(".jar")) {
        File jar = new File(classpathEntry);

        JarInputStream is = new JarInputStream(new FileInputStream(jar));

        JarEntry entry;
        while( (entry = is.getNextJarEntry()) != null) {
            if(entry.getName().endsWith(".class")) {
                // Class.forName(entry.getName()) and check
                //   for implementation of the interface
            }
        }
    }
}

संपादित करें: जॉनस्टोक सही है (टिप्पणियों में) कि यह केवल स्टैंडअलोन जावा अनुप्रयोगों के लिए काम करता है, और एक एप्लिकेशन सर्वर के तहत काम नहीं करेगा।


यह तब अच्छी तरह से काम नहीं करता है जब अन्य साधनों से कक्षाएं भरी जाती हैं, उदाहरण के लिए, वेब या J2EE कंटेनर में।
जॉन्स्टोक

आप शायद एक बेहतर काम कर सकते हैं, यहां तक ​​कि एक कंटेनर के नीचे भी, अगर आपने URL[]क्लाथलाडर पदानुक्रम से रास्तों (अक्सर लेकिन हमेशा ऐसा नहीं हो सकता है तो संभव नहीं है) को बंद कर दिया। अक्सर यद्यपि आपके पास अनुमति होनी चाहिए क्योंकि आमतौर पर आपके पास SecurityManagerजेवीएम के भीतर लोड होता है ।
केविन ब्रॉक

मेरे लिए एक जादू की तरह काम किया! मुझे डर था कि यह बहुत भारी वजन और धीमा होगा, क्लासपाथ में सभी कक्षाओं के माध्यम से जा रहा है, लेकिन वास्तव में मैंने उन फाइलों को सीमित कर दिया है जिन्हें मैंने "-जेबीबी" या अन्य पहचानने योग्य फ़ाइल नामों से जोड़ा है, और यह अभी भी शुरू होने की तुलना में 100 गुना तेज है। एक एम्बेडेड ग्लासफिश या EJBContainer। अपनी विशेष स्थिति में, मैंने तब "स्टेटलेस", "स्टेटफुल", और "सिंगलटन" एनोटेशन की तलाश में कक्षाओं का विश्लेषण किया, और अगर मैंने उन्हें देखा, तो मैंने अपने MockInitialConteleFactory में JNDI मैप्ड नाम जोड़ा। एक बार फिर धन्यवाद!
cs94njw

4

यदि आप किसी मौजूदा कोड को रीफ़्लेक्ट किए बिना कुछ सरल और त्वरित चाहते हैं, तो आप स्ट्राइप्स फ्रेमवर्क से ResolverUtil ( कच्चा स्रोत ) का उपयोग कर सकते हैं ।

यहां कोई भी वर्ग लोड नहीं होने का एक सरल उदाहरण दिया गया है:

package test;

import java.util.Set;
import net.sourceforge.stripes.util.ResolverUtil;

public class BaseClassTest {
    public static void main(String[] args) throws Exception {
        ResolverUtil<Animal> resolver = new ResolverUtil<Animal>();
        resolver.findImplementations(Animal.class, "test");
        Set<Class<? extends Animal>> classes = resolver.getClasses();

        for (Class<? extends Animal> clazz : classes) {
            System.out.println(clazz);
        }
    }
}

class Animal {}
class Dog extends Animal {}
class Cat extends Animal {}
class Donkey extends Animal {}

यह एक एप्लिकेशन सर्वर में भी काम करता है और साथ ही यह काम करने के लिए डिज़ाइन किया गया था;)

कोड मूल रूप से निम्न कार्य करता है:

  • आपके द्वारा निर्दिष्ट पैकेज में सभी संसाधनों पर पुनरावृति
  • केवल .class में समाप्त होने वाले संसाधनों को रखें
  • उन वर्गों का उपयोग करके लोड करें ClassLoader#loadClass(String fullyQualifiedName)
  • अगर जाँच करता है Animal.class.isAssignableFrom(loadedClass);

4

किसी दिए गए वर्ग के सभी उपवर्गों को सूचीबद्ध करने के लिए सबसे मजबूत तंत्र वर्तमान में क्लासग्राफ है , क्योंकि यह नए जेपीएमएस मॉड्यूल सिस्टम सहित क्लासपैथ विनिर्देशन तंत्र के व्यापक संभव सरणी को संभालता है। (मैं लेखक हूं)

List<Class<Animal>> animals;
try (ScanResult scanResult = new ClassGraph().whitelistPackages("com.zoo.animals")
        .enableClassInfo().scan()) {
    animals = scanResult
        .getSubclasses(Animal.class.getName())
        .loadClasses(Animal.class);
}

परिणाम प्रकार की सूची है <क्लास <पशु >>
hiaclibe

2

जावा गतिशील रूप से कक्षाओं को लोड करता है, इसलिए आपकी कक्षाओं का ब्रह्मांड केवल वही होगा जो पहले से लोड किया गया है (और अभी तक अनलोड नहीं है)। शायद आप एक कस्टम क्लास लोडर के साथ कुछ कर सकते हैं जो प्रत्येक लोडेड क्लास के सुपरपाइप्स की जांच कर सकता है। मुझे नहीं लगता कि भरी हुई कक्षाओं के सेट को क्वेरी करने के लिए कोई एपीआई है।


2

इसे इस्तेमाल करो

public static Set<Class> getExtendedClasses(Class superClass)
{
    try
    {
        ResolverUtil resolver = new ResolverUtil();
        resolver.findImplementations(superClass, superClass.getPackage().getName());
        return resolver.getClasses();  
    }
    catch(Exception e)
    {Log.d("Log:", " Err: getExtendedClasses() ");}

    return null;
}

getExtendedClasses(Animals.class);

संपादित करें:


2
आपको ResolverUtil के बारे में कुछ और कहना होगा। यह क्या है? यह किस पुस्तकालय से आता है?
जॉनीलैम्बडा

1

इस सवाल का जवाब देने वाले सभी को धन्यवाद।

ऐसा लगता है कि यह वास्तव में दरार करने के लिए एक कठिन अखरोट है। मैंने अपनी बेसकलैस में एक स्टैटिक ऐरे और गेट्टर बनाने का काम छोड़ दिया।

public abstract class Animal{
    private static Animal[] animals= null;
    public static Animal[] getAnimals(){
        if (animals==null){
            animals = new Animal[]{
                new Dog(),
                new Cat(),
                new Lion()
            };
        }
        return animals;
    }
}

ऐसा लगता है कि जावा बस स्वयं की खोज के लिए सेट नहीं है जिस तरह से सी # है। मुझे लगता है कि समस्या यह है कि चूंकि एक जावा ऐप सिर्फ एक निर्देशिका / जार फ़ाइल में .class फ़ाइलों का एक संग्रह है, रनटाइम एक वर्ग के बारे में नहीं जानता है जब तक कि इसे संदर्भित नहीं किया जाता है। उस समय लोडर इसे लोड करता है - जो मैं करने की कोशिश कर रहा हूं वह मुझे इसे संदर्भित करने से पहले पता चलता है जो कि फ़ाइल सिस्टम पर जाने और देखने के बिना संभव नहीं है।

मुझे हमेशा ऐसा कोड पसंद है जो मेरे बारे में बताने के बजाय खुद को खोज सके, लेकिन अफसोस यह काम भी करता है।

एक बार फिर धन्यवाद!


1
जावा स्व-खोज के लिए स्थापित है। आपको बस थोड़ा और काम करने की जरूरत है। विवरण के लिए मेरा उत्तर देखें।
डेव जार्विस

1

OpenPojo का उपयोग करके आप निम्नलिखित कार्य कर सकते हैं:

String package = "com.mycompany";
List<Animal> animals = new ArrayList<Animal>();

for(PojoClass pojoClass : PojoClassFactory.enumerateClassesByExtendingType(package, Animal.class, null) {
  animals.add((Animal) InstanceFactory.getInstance(pojoClass));
}

0

यह एक कठिन समस्या है और आपको स्थैतिक विश्लेषण का उपयोग करके इस जानकारी का पता लगाने की आवश्यकता होगी, यह रनटाइम पर आसानी से उपलब्ध नहीं है। मूल रूप से अपने ऐप का क्लासपाथ प्राप्त करें और उपलब्ध कक्षाओं के माध्यम से स्कैन करें और जिस कक्षा से यह विरासत में आता है, उस कक्षा की बायोटेक जानकारी पढ़ें। ध्यान दें कि एक वर्ग कुत्ता सीधे पशु से विरासत में नहीं मिल सकता है, लेकिन पालतू से विरासत में मिला हो सकता है जो पशु से विरासत में मिला है, इसलिए आपको उस पदानुक्रम का ट्रैक रखने की आवश्यकता होगी।


0

एक तरीका यह है कि कक्षाएं स्टैटिक इनिशियलाइज़र का उपयोग करें ... मुझे नहीं लगता कि ये विरासत में मिली हैं (यदि वे हैं तो यह काम नहीं करेगा):

public class Dog extends Animal{

static
{
   Animal a = new Dog();
   //add a to the List
}

आपको इस कोड को सभी वर्गों में शामिल करना होगा। लेकिन यह कहीं न कहीं एक बड़ा बदसूरत लूप होने से बचता है, पशु के बच्चों की खोज करने वाले हर वर्ग का परीक्षण करता है।


3
यह मेरे मामले में काम नहीं करता है। चूंकि वर्ग को कहीं भी संदर्भित नहीं किया जाता है, इसलिए इसे कभी भी लोड नहीं किया जाता है, इसलिए स्थिर इनिशियलाइज़र को कभी नहीं कहा जाता है। ऐसा लगता है कि जब क्लास लोड होती है तो एक स्टैटिक इनिशियलाइज़र को बाकी सब चीजों से पहले बुलाया जाता है - लेकिन मेरे मामले में क्लास कभी लोड नहीं होती है।
जॉनीलांबाडा

0

मैंने पैकेज लेवल एनोटेशंस का उपयोग करते हुए इस समस्या को बहुत सुंदर तरीके से हल किया और फिर उस एनोटेशन को एक तर्क के रूप में कक्षाओं की सूची बना दिया।

एक इंटरफ़ेस को लागू करने वाली जावा कक्षाएं ढूंढें

कार्यान्वयन के लिए सिर्फ एक पैकेज-info.java बनाना है और उन कक्षाओं की सूची के साथ जादू एनोटेशन डालना है जो वे समर्थन करना चाहते हैं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.