2015-06-16 12 views
8

है, मैं एक कोड विश्लेषक को लागू करने की कोशिश कर रहा हूं जो एक और जावा फ़ाइल इनपुट के रूप में लेता है। प्रत्येक परिवर्तनीय घोषणा के लिए, मैं यह जांचना चाहता हूं कि चर का प्रकार सामान्य है या नहीं। क्या ऐसा करने का कोई आसान तरीका है? तोजांचें कि सामान्य जेनेरिक

isGenericType("HashSet") -> true 
isGenericType("int") -> false 

मैं संभवतः एक रजिस्ट्री सब सामान्य प्रकार युक्त बना सकते हैं, लेकिन अगर मैं एक कस्टम सामान्य प्रकार लागू है कि के साथ समस्या यह हो सकता है, मैं होगा:

उदाहरण के लिए, मैं इसके लिए चाहते हैं प्रत्येक बार रजिस्ट्री को अद्यतन करना होगा। क्या इसका कोई आसान समाधान है?

+1

क्या आप उन प्रकारों की पहचान करना चाहते हैं जिनमें उनकी घोषणा में एक या अधिक प्रकार के पैरामीटर हैं? या एक प्रकार के सामान्य उपयोग की पहचान करने के लिए? उदाहरण के लिए, @manouti के रूप में, हैशसेट को एक प्रकार पैरामीटर के साथ परिभाषित किया गया है, लेकिन इसे कच्चे तरीके से उपयोग किया जा सकता है। तो क्या आप वास्तव में जेनेरिक के रूप में मूल्यांकन करने के लिए कच्चे हैशसेट का उपयोग करके एक परिवर्तनीय घोषणा चाहते हैं? –

+0

नहीं, इसे इंगित करने के लिए धन्यवाद। जब मैं एक टाइप किए गए पैरामीटर के साथ हैशसेट को परिभाषित करता हूं तो मैं केवल सत्य वापस करना चाहता हूं। – akhiljain

उत्तर

6

यहां तक ​​कि अगर HashSet सामान्य हो सकता है, प्रकार HashSet ही (<T> के बिना) एक कच्चे प्रकार है। इसलिए, मैं घोषित चर के वास्तविक प्रकार स्कैनिंग, हो सकता है प्रकार पर एक regex लागू करने को देखने के लिए की दृष्टिकोण के साथ जाना होगा अगर कोण कोष्ठक मौजूद हैं:

isGenericType --> matches the pattern [a-zA-Z_\$][\w\$]*<[a-zA-Z_\$][\w\$]*> 

आप सख्ती से एक वैध पहचानकर्ता के लिए खाते में चाहते हैं , आप Java Language Specification में इसकी परिभाषा देख सकते हैं:

पहचानकर्ता:

IdentifierChars नहीं बल्कि एक कीवर्ड या बो oleanLiteral या NullLiteral

IdentifierChars:

JavaLetter {JavaLetterOrDigit}

JavaLetter:

किसी भी यूनिकोड वर्ण है कि एक "जावा पत्र" है

JavaLetterOrDigit:

किसी भी यूनिकोड वर्ण है कि एक "जावा पत्र या अंकों"

है

ए "जावा लेटर" एक ऐसा चरित्र है जिसके लिए विधि Character.isJavaIdentifierStart(int) सत्य लौटाती है।

ए "जावा अक्षर-या-अंक" एक वर्ण है जिसके लिए विधि Character.isJavaIdentifierPart(int) सत्य लौटाती है।

+1

रेगेक्स को ध्यान में रखना चाहिए कि कक्षा के नाम में अंक 0-9, _ अंडरस्कोर, और $ डॉलर के संकेत हो सकते हैं। – MusicMaster

+1

यह नहीं होना चाहिए, '[ए-जेए-जेड _ \ $] + [ए-जेए-जेड -9 _ \ $] * [\ w] * <[\ w] * [a-zA-Z _ \ $] + [ए-जेए-जेड -9 _ \ $] * [\ w] *> '? – MusicMaster

+0

@MusicMaster मेरा regex बिल्कुल सही सटीक होने का इरादा नहीं है, बस विचार बता रहा है। – manouti

5

मुझे लगता है कि यह आपके लिए क्या देख रहे के समान है:

यह java.util.HashSet के लिए true प्रिंट करता है।

लेकिन java.lang.Object के लिए false

public class TestGenerics 
{ 
    public static boolean isGenericType(String s) throws ClassNotFoundException 
    { 
     Class c = Class.forName(s); 
     return c.getTypeParameters().length > 0; 
    } 

    public static void main(String[] args) throws ClassNotFoundException 
    { 
     System.out.println(isGenericType("java.util.HashSet")); 
     System.out.println(isGenericType("java.lang.Object")); 
    } 
} 
+2

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

+0

यह उत्तर, छोटे और सरल होने पर, कस्टम प्रकारों के लिए काम नहीं करता है जब तक कि @Erick G. Hagstrom ने इंगित किया कि इसके बाइटकोड तक पहुंच भी है। मैं आदर्श रूप से जावा स्रोत फ़ाइल पर इस कोड विश्लेषक को चलाने के लिए चाहता हूं क्योंकि निष्पादन योग्य आईएमओ बस खराब हैं! – akhiljain

4

यह ऑब्जेक्ट, क्लास या क्लास नाम द्वारा परीक्षण करेगा।

अद्यतन: सहायक टिप्पणियों के आधार पर इस कोड के कई संशोधनों ने कुछ दिलचस्प परीक्षण मामले प्रदान किए हैं। हालांकि, अंततः समस्या का एक उचित समाधान इस बात पर निर्भर करता है कि सवाल कैसे परिभाषित किया गया है। पहले संशोधन और टिप्पणियों का संदर्भ लें।

import java.lang.reflect.ParameterizedType; 
import java.lang.reflect.Type; 
import java.util.HashMap; 

public class GenericTest 
{ 
    public static void main(String[] args) 
    { 
     try 
     { 
      new GenericTest().testAll(); 
     } catch (Exception e) { 
      e.printStackTrace(); 
      System.exit(1); 
     } 

     System.exit(0); 
    } 

    public void testAll() throws ClassNotFoundException, InstantiationException, IllegalAccessException 
    { 
     Object a = new HashMap<String, Object>(); 
     Object b = new HashMap(); 
     int c = 0; 

     isGeneric(a); 
     System.out.println("\n"); 
     isGeneric(b); 
     System.out.println("\n"); 
     isGeneric(c); 
     System.out.println("\n"); 
     isGeneric("java.util.HashMap"); 
     System.out.println("\n"); 
     isGeneric("java.lang.Integer"); 
     System.out.println("\n"); 

     isGeneric(new TestA()); 
     System.out.println("\n"); 
     isGeneric(new TestB()); 
     System.out.println("\n"); 
     isGeneric(new TestB<String>()); 
     System.out.println("\n"); 
     isGeneric(new TestC()); 
     System.out.println("\n"); 
     isGeneric(new TestD()); 
     System.out.println("\n"); 
     isGeneric(new TestE()); 
     System.out.println("\n"); 

     return; 
    } 

    public static void isGeneric(String className) throws ClassNotFoundException, InstantiationException, IllegalAccessException 
    { 
     GenericTest.isGeneric(Class.forName(className)); 
     return; 
    } 

    public static boolean isGeneric(Object o) 
    { 
     return isGeneric(o.getClass()); 
    } 

    public static boolean isGeneric(Class<?> c) 
    { 
     boolean hasTypeParameters = hasTypeParameters(c); 
     boolean hasGenericSuperclass = hasGenericSuperclass(c); 
//  boolean hasGenericSuperinterface = hasGenericSuperinterface(c); 
//  boolean isGeneric = hasTypeParameters || hasGenericSuperclass || hasGenericSuperinterface; 
     boolean isGeneric = hasTypeParameters || hasGenericSuperclass; 

     System.out.println(c.getName() + " isGeneric: " + isGeneric); 

     return isGeneric; 
    } 

    public static boolean hasTypeParameters(Class<?> c) 
    { 
     boolean flag = c.getTypeParameters().length > 0; 
     System.out.println(c.getName() + " hasTypeParameters: " + c.getTypeParameters().length); 
     return flag; 
    } 

    public static boolean hasGenericSuperclass(Class<?> c) 
    { 
     Class<?> testClass = c; 

     while (testClass != null) 
     { 
      Type t = testClass.getGenericSuperclass(); 

      if (t instanceof ParameterizedType) 
      { 
       System.out.println(c.getName() + " hasGenericSuperclass: " + t.getClass().getName()); 
       return true; 
      } 

      testClass = testClass.getSuperclass(); 
     } 

     return false; 
    } 

    public static boolean hasGenericSuperinterface(Class<?> c) 
    { 
     for (Type t : c.getGenericInterfaces()) 
     { 
      if (t instanceof ParameterizedType) 
      { 
       System.out.println(c.getName() + " hasGenericSuperinterface: " + t.getClass().getName()); 
       return true; 
      } 
     } 

     return false; 
    } 

    public interface TestX<X> { } 

    public interface TestY extends TestX<String> { } 

    public class TestA implements TestY { } 

    public class TestB<V> extends TestA { } 

    public class TestC extends TestB<String> { } 

    public class TestD extends TestA { } 

    public class TestE extends TestC { } 
} 

ऊपर कोड चलाने का परिणाम:

java.util.HashMap hasTypeParameters: 2 
java.util.HashMap hasGenericSuperclass: sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl 
java.util.HashMap isGeneric: true 


java.util.HashMap hasTypeParameters: 2 
java.util.HashMap hasGenericSuperclass: sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl 
java.util.HashMap isGeneric: true 


java.lang.Integer hasTypeParameters: 0 
java.lang.Integer isGeneric: false 


java.util.HashMap hasTypeParameters: 2 
java.util.HashMap hasGenericSuperclass: sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl 
java.util.HashMap isGeneric: true 


java.lang.Integer hasTypeParameters: 0 
java.lang.Integer isGeneric: false 


GenericTest$TestA hasTypeParameters: 0 
GenericTest$TestA isGeneric: false 


GenericTest$TestB hasTypeParameters: 1 
GenericTest$TestB isGeneric: true 


GenericTest$TestB hasTypeParameters: 1 
GenericTest$TestB isGeneric: true 


GenericTest$TestC hasTypeParameters: 0 
GenericTest$TestC hasGenericSuperclass: sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl 
GenericTest$TestC isGeneric: true 


GenericTest$TestD hasTypeParameters: 0 
GenericTest$TestD isGeneric: false 


GenericTest$TestE hasTypeParameters: 0 
GenericTest$TestE hasGenericSuperclass: sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl 
GenericTest$TestE isGeneric: true 
+1

यह जांचता है कि सुपरक्लास एक पैरामीटरेट टाइप है या नहीं। ओपी जानना चाहता है कि कक्षा स्वयं "जेनेरिक" है (जिसका अर्थ पैरामीटर किया जा सकता है, मुझे अभी तक यकीन नहीं है)। पैरामीटरेटेड टाइप का सी उदाहरण क्यों न दें? –

+2

@ ErickG.Hagstrom अच्छा बिंदु। आप 'getGenericSuperclass()' के बारे में सही हैं, हालांकि, सी उदाहरण के पैरामीटरेट टाइप टाइप के परिणामस्वरूप कच्चे वर्ग के खिलाफ संकलन त्रुटि होगी: 'असंगत सशर्त ऑपरेंड प्रकार क्लास और पैरामीटरेटेड टाइप'। मैंने यूजीनिया द्वारा प्रदान किए गए उत्तर में जेनेरिक पैरामीटर की संख्या के आधार पर परीक्षण के लिए अपना उदाहरण संशोधित किया। हालांकि, एक पूर्ण समाधान सुपरक्लास का परीक्षण भी कर सकता है (ऊपर टेस्टसी का मामला देखें, जहां "सामान्य नहीं है" लेकिन इसका सुपरक्लास एक सामान्य है)। – vallismortis

+1

ठीक है, सी उदाहरण के बारे में जानना अच्छा है पैरामीटरेट टाइप टाइप नहीं कर रहा है। प्रकार के पैरामीटर का उपयोग करने के लिए समझ में आता है। लेकिन अगर आप सुपरक्लास का परीक्षण करना चाहते हैं, तो क्या आप पूरे पदानुक्रम (सुपरक्लास के सुपरक्लास, इंटरफेस लागू किए गए और उनके सुपरर्स) का परीक्षण नहीं करना चाहते हैं? –

3

अन्य उत्तर रेगुलर एक्सप्रेशन या प्रतिबिंब का उपयोग पर ध्यान केंद्रित कर रहे हैं, लेकिन क्या इस के लिए कॉल करने के लिए लगता है एक पार्सर है। तो मैं आपको सलाह देता हूं कि आप इसका इस्तेमाल करें।

उदाहरण के लिए, ग्रहण उनके जावा विकास उपकरण (जेडीटी) प्लग-इन है जो आप पार्स उपकरण आपको यह अधिकार क्या करने की जरूरत होगी के सभी के साथ प्रदान करता है। आपको बस इतना करना होगा जेडीटी पूछना बाइंडिंग सक्षम के साथ आप एक सार सिंटेक्स पेड़ (एएसटी) दे रहा है। जब भी एक चर घोषित किया जाता है आप चर का घोषित प्रकार के लिए एक ITypeBinding मिल चाहते हैं, और instantiated प्रकार (यदि चर घोषणा में instantiated है) के लिए एक और।

और ITypeBinding तरीकों कि आपको बता कि क्या यह सामान्य, पैरामिट्रीकृत है, आदि

तुम भी मामले में आप उन की जरूरत प्रकार पैरामीटर मिलेगा।

रूप में अच्छी तरह अन्य जावा पार्सर विकल्प हैं, लेकिन यह एक जिसके साथ मैं परिचित हूँ है।

=============================================

विशिष्ट परिणाम ग्रहण जेडीटी

केस 1 का उपयोग कर: HashSet<String> h1 = new HashSet();

केस 2: HashSet<String> h2 = new HashSet<>();

केस 3: HashSet<String> h3 = new HashSet<String>();

के रूप में वर्तमान में समझा, इस अभ्यास का लक्ष्य प्रकरण पहचान करने के लिए है 1 के रूप में नहीं जेनेरिक (यह कच्चे है), और मामले में 2 और सामान्य रूप में 3 (वे प्रकार पैरामीटर है, हालांकि केस 2 में प्रकार पैरामीटर निहित है)।

new HashSet...

सभी तीन मामलों से उत्पादन नोड्स पर पूरी तरह से देख रहे हैं एक ClassInstanceCreation उदाहरण का उत्पादन।

केस 1 और केस 2 में 0 प्रकार के तर्क हैं, जबकि केस 3 में 1 प्रकार का तर्क है।

केस 1 के लिए आईटीपी बाइंडिंग कच्चे चिह्नित है, जबकि मामलों 2 और 3 के लिए उन्हें पैरामीटरकृत चिह्नित किया गया है। यह सफलता बनाता है, क्योंकि हमारे पास एक तरफ केस 1 और दूसरे मामले 2 और 3 के बीच अंतर करने का एक तरीका है।

केस 1 के लिए आईटीपी बाइंडिंग में 0 प्रकार के तर्क हैं, जबकि मामले 2 और 3 दोनों में IType बाइंडिंग में 1 प्रकार का तर्क है। ध्यान दें कि केस 2 में एएसटी में 0 प्रकार के तर्क हैं, लेकिन बाइंडिंग में एक प्रकार का तर्क है। यह एक स्पष्ट प्रकार तर्क और हीरा ऑपरेटर के उपयोग के बीच अंतर करने की अनुमति देता है।

इस कार्य को पूरा करने के लिए आवश्यक सभी जानकारी एएसटी और बाइंडिंग से आसानी से उपलब्ध है।

अब यहां बुरी खबर है: बाइंडिंग केवल तभी उपलब्ध होती है जब बाइट कोड उपलब्ध हो, यानी यह एक साधारण पाठ विश्लेषण अभ्यास नहीं है।

+0

मैंने वास्तव में एएसटी प्राप्त करने के लिए लंबोक पार्सर का उपयोग करने की कोशिश की। लेकिन ऐसा लगता है कि उनके पास टाइप पैरामीटर के लिए एक अलग नोड नहीं है। इसके अलावा, मैं मूल रूप से instantiations के निम्नलिखित तीन प्रकार के बीच अंतर करना चाहता था: 'HashSet एच = नए HashSet()' ' HashSet एच = नए HashSet <>()' ' HashSet एच = नए HashSet () ' मैंने जिन पार्सर्स को देखा है, वे पहले दो मामलों को एएसटी – akhiljain

+1

में समान प्रतिनिधित्व में कम करते हैं, मैंने आपको यह दिखाने के लिए उत्तर अपडेट किया कि आप ग्रहण जेडीटी में आईटीपी बाइंडिंग से क्या प्राप्त कर सकते हैं। कृपया ध्यान दें कि बाइंडिंग केवल तभी उपलब्ध होती है जब बाइट कोड पहुंच योग्य होता है, इसलिए उस संबंध में प्रतिबिंब से बेहतर नहीं है। लेकिन यह प्रतिबिंब से बेहतर है कि हम एक कच्चे तत्काल और हीरा ऑपरेटर का उपयोग कर अंतर कर सकते हैं। –

संबंधित मुद्दे