एक सामान्य वर्ग में स्थैतिक विधि?


197

जावा में, मैं कुछ इस प्रकार रखना चाहूंगा:

class Clazz<T> {
  static void doIt(T object) {
    // ...
  }
}

लेकिन मुझे मिलता है

गैर-स्थैतिक प्रकार T के लिए एक स्थिर संदर्भ नहीं बनाया जा सकता है

मैं सामान्य उपयोगों से परे जेनरिक को नहीं समझता और इस तरह इसका बहुत अर्थ नहीं बना सकता। यह मदद नहीं करता है कि मैं इस विषय के बारे में इंटरनेट पर बहुत जानकारी नहीं पा रहा था।

क्या कोई स्पष्ट कर सकता है कि क्या इस तरह का उपयोग संभव है, इसी तरह से? इसके अलावा, मेरा मूल प्रयास असफल क्यों था?

जवाबों:


270

आप स्थैतिक विधियों या स्थिर क्षेत्रों में एक वर्ग के सामान्य प्रकार के मापदंडों का उपयोग नहीं कर सकते। श्रेणी के प्रकार के पैरामीटर केवल उदाहरण के तरीकों और उदाहरण के क्षेत्रों के लिए दायरे में हैं। स्थिर क्षेत्रों और स्थिर तरीकों के लिए, उन्हें कक्षा के सभी उदाहरणों के बीच साझा किया जाता है, यहां तक ​​कि विभिन्न प्रकार के मापदंडों के उदाहरण भी, इसलिए स्पष्ट रूप से वे एक विशेष प्रकार के पैरामीटर पर निर्भर नहीं हो सकते हैं।

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


9
उलटा, यह जवाब वास्तव में सिर्फ एक समाधान प्रदान करने के बजाय पोस्टर की समस्या की व्याख्या करता है।
Jorn

7
@Andre: आपका अंतर्ज्ञान निराधार नहीं है; सी # वास्तव में इस तरह से जेनरिक का इलाज करता है।
ज्योन्गदेव

32
"स्थिर क्षेत्रों और स्थिर तरीकों के लिए, उन्हें कक्षा के सभी उदाहरणों के बीच साझा किया जाता है, यहां तक ​​कि विभिन्न प्रकार के मापदंडों के उदाहरण भी ..." आउच! प्रकार में पागल द्वारा फिर से मिटा दिया!
रिविलहिल

2
यदि आप देखेंगे कि संकलन के बाद सामान्य वर्ग / तरीके कैसे दिखते हैं, तो आप देखेंगे कि जेनेरिक विशेषता को हटा दिया गया है। और सूची <पूर्णांक> संकलन के बाद "सूची" जैसा दिखता है। इसलिए संकलन के बाद सूची <Integer> और List <Long> के बीच कोई अंतर नहीं है - दोनों ही सूची बन गई।
डेनियस

3
मुझे लगता है कि उन्होंने समझाया कि वह क्या करना चाह रहे हैं। यह स्पष्ट है कि वह डाट बूटी को हिलाने की कोशिश कर रहा है! हां
astryk

140

जावा को नहीं पता है कि Tजब तक आप एक प्रकार का पलटा नहीं लेते हैं।

हो सकता है कि आप कॉल करके स्थिर तरीकों को निष्पादित कर सकते हैं, Clazz<T>.doit(something)लेकिन ऐसा लगता है कि आप नहीं कर सकते।

चीजों को संभालने का दूसरा तरीका यह है कि टाइप पैरामीटर को विधि में ही रखा जाए:

static <U> void doIt(U object)

जो आपको यू पर सही प्रतिबंध नहीं मिलता है, लेकिन यह कुछ भी नहीं से बेहतर है ...।


तब मैं बिना किसी अड़चन के, अर्थात Clazz.doIt (वस्तु) को निर्दिष्ट किए बिना विधि को कॉल करूंगा बजाय Clazz <Object> .doIt (ऑब्जेक्ट), सही? क्या आप इसे ठीक मानते हैं?
एंड्रे चलेला

दूसरा सिंटैक्स एक अधिक सटीक होता है, जिसकी आपको आवश्यकता होती है यदि कंपाइलर उस कंटैक्स से रिटर्न मान के प्रकार का अनुमान नहीं लगा सकता है जिसमें विधि कहा जाता है। दूसरे शब्दों में, यदि संकलक Clazz.doIt (ऑब्जेक्ट) की अनुमति देता है, तो ऐसा करें।
19-40

1
मैंने क्लैज़ <ऑब्जेक्ट> .doIt (ऑब्जेक्ट) की कोशिश की और एक संकलन-समय त्रुटि मिली! "टोकन (ओं) पर सिंटैक्स त्रुटि, गलत निर्माण (ओं)"। Clazz.doIt (ऑब्जेक्ट) ठीक काम करता है, हालांकि चेतावनी भी नहीं।
एंड्रे चलेला

8
Clazz का उपयोग करें। <ऑब्जेक्ट> doIt (ऑब्जेक्ट)। मुझे लगता है कि यह अजीब वाक्यविन्यास है, C ++ के क्लैज़ <int> :: doIt (1) की तुलना में
क्राउन किंग

आप यह क्यों कहते हैं कि इससे आपको U पर सही प्रतिबंध नहीं लगता है?
एडम बर्ली

46

मैं इसी समस्या में भाग गया। मुझे Collections.sortजावा फ्रेमवर्क में स्रोत कोड डाउनलोड करके अपना उत्तर मिला । मैंने जो उत्तर दिया, वह <T>जेनेरिक पद्धति में था, न कि कक्षा की परिभाषा में।

तो यह काम किया:

public class QuickSortArray  {
    public static <T extends Comparable> void quickSort(T[] array, int bottom, int top){
//do it
}

}

बेशक, ऊपर दिए गए उत्तरों को पढ़ने के बाद मैंने महसूस किया कि यह सामान्य वर्ग का उपयोग किए बिना एक स्वीकार्य विकल्प होगा:

public static void quickSort(Comparable[] array, int bottom, int top){
//do it
}

8
जबकि स्वीकृत उत्तर तकनीकी रूप से सही था: यह वास्तव में वही है जो मैं तब खोज रहा था जब Google ने मुझे इस प्रश्न के लिए प्रेरित किया था।
जेरेमी सूची

T extends XXXसिंटैक्स में एक उदाहरण प्रदान करने के लिए सहारा ।
ओगरे Psalm33

3
@ जब से आप वैसे भी जेनरिक का उपयोग कर रहे हैं, तो आप सभी तरह से उनका उपयोग कर सकते हैं --- जैसे कच्चे प्रकार का उपयोग नहीं करना Comparable<T extends Comparable<? super T>>इसके बजाय कोशिश करें ।
easoncxz

15

यह सामान्य तरीकों के लिए सिंटेक्स के उपयोग से आप क्या चाहते करने के लिए जब अपने घोषित करने के लिए संभव है doIt()विधि (के अलावा के नोटिस <T>के बीच staticऔर voidकी विधि हस्ताक्षर में doIt()):

class Clazz<T> {
  static <T> void doIt(T object) {
    // shake that booty
  }
}

मुझे ग्रहण संपादक को Cannot make a static reference to the non-static type Tत्रुटि के बिना उपरोक्त कोड को स्वीकार करने के लिए मिला और फिर इसे निम्नलिखित कार्य कार्यक्रम में विस्तारित किया (कुछ हद तक उपयुक्त सांस्कृतिक गतिविधियों के साथ):

public class Clazz<T> {
  static <T> void doIt(T object) {
    System.out.println("shake that booty '" + object.getClass().toString()
                       + "' !!!");
  }

  private static class KC {
  }

  private static class SunshineBand {
  }

  public static void main(String args[]) {
    KC kc = new KC();
    SunshineBand sunshineBand = new SunshineBand();
    Clazz.doIt(kc);
    Clazz.doIt(sunshineBand);
  }
}

जब मैं इसे चलाता हूं तो ये रेखाएं कंसोल को प्रिंट करती हैं:

उस बूटी को हिलाओ 'क्लास com.eclipseoptions.datamanager.Clazz $ KC' !!!
उस बूटी को हिलाओ 'क्लास com.eclipseoptions.datamanager.Clazz $ SunshineBand' !!!


इस मामले में, क्या दूसरा <T> पहले वाले को छिपाता है?
आंद्रे चैलेला

1
@ एंड्रेवेस, हाँ। दूसरा <T>मास्क उसी तरह से पहला होता है class C { int x; C(int x) { ... } }जिस पैरामीटर xमें क्षेत्र मास्क करता है x
माइक सैमुअल

नमूने के आउटपुट की व्याख्या कैसे करें: ऐसा लगता है कि इसमें वास्तव में दो प्रकार शामिल हैं, एक पैरामीटर टी मान द्वारा? यदि T वास्तव में वर्ग प्रकार छुपा रहा था, तो मुझे उम्मीद है कि "booty 'क्लास com.eclipseoptions.datamanager.Clazz" मापदंडों के बिना दो बार आउटपुट किया गया।
प्रागमेटेक

1
इसका मास्किंग से कोई लेना-देना नहीं है। एक स्थैतिक विधि बस वर्गों सामान्य प्रकार से बाध्य नहीं किया जा सकता है । फिर भी यह अपने सामान्य प्रकार और सीमाओं को परिभाषित कर सकता है।
माइक

क्या स्थैतिक प्रकार को एक बार परिभाषित करने और कई स्थिर तरीकों के लिए इसका पुन: उपयोग करने का कोई तरीका है? static <E extends SomeClass> E foo(E input){return input;}जब मैं कुछ करना चाहता हूं, तो मैं हर तरीके के लिए प्रशंसक नहीं हूंstatic <E extends SomeClass>; //static generic type defined only once and reused static E foo(E input){return input;} static E bar(E input){return input;} //...etc...
HesNotTheStig

14

मुझे लगता है कि इस सिंटैक्स का अभी तक उल्लेख नहीं किया गया है (मामले में आप बिना तर्क के एक विधि चाहते हैं):

class Clazz {
  static <T> T doIt() {
    // shake that booty
  }
}

और कॉल:

String str = Clazz.<String>doIt();

आशा है कि यह किसी की मदद करेंगे।


1
वास्तव में (मुझे जावा संस्करण पता नहीं है), यह बिना भी संभव है <String>क्योंकि यह केवल टाइप तर्क को संक्रमित करता है क्योंकि आप इसे एक चर पर असाइन करते हैं। यदि आप इसे असाइन नहीं करते हैं, तो यह आसानी से होता है Object
Adowrath

यह एक सही समाधान है।
प्रभात रंजन

6

यह त्रुटि में सही रूप से उल्लिखित है: आप गैर-स्थैतिक प्रकार टी के लिए एक स्थिर संदर्भ नहीं बना सकते हैं। इसका कारण यह है कि टाइप पैरामीटर Tको किसी भी प्रकार के तर्क द्वारा प्रतिस्थापित किया जा सकता है जैसे Clazz<String>या Clazz<integer>आदि। लेकिन स्थिर फ़ील्ड / तरीके सभी गैर द्वारा साझा किए जाते हैं वर्ग की अस्थिर वस्तुएं।

निम्नलिखित अंश डॉक से लिया गया है :

एक क्लास का स्टैटिक फील्ड एक क्लास-लेवल वैरिएबल है जिसे क्लास के सभी नॉन-स्टैटिक ऑब्जेक्ट्स द्वारा साझा किया जाता है। इसलिए, प्रकार के मापदंडों के स्थिर क्षेत्रों की अनुमति नहीं है। निम्नलिखित वर्ग पर विचार करें:

public class MobileDevice<T> {
    private static T os;

    // ...
}

यदि प्रकार मापदंडों के स्थिर क्षेत्रों की अनुमति दी गई थी, तो निम्न कोड भ्रमित होगा:

MobileDevice<Smartphone> phone = new MobileDevice<>();
MobileDevice<Pager> pager = new MobileDevice<>();
MobileDevice<TabletPC> pc = new MobileDevice<>();

क्योंकि स्थैतिक क्षेत्र os को फोन, पेजर और पीसी द्वारा साझा किया जाता है, वास्तविक प्रकार का ओएस क्या है? यह एक ही समय में स्मार्टफोन, पेजर और टैबलेटपीसी नहीं हो सकता है। इसलिए, आप प्रकार पैरामीटर के स्थिर फ़ील्ड नहीं बना सकते हैं।

जैसा कि उनके जवाब में क्रिस द्वारा सही ढंग से कहा गया है कि आपको इस मामले में कक्षा के साथ विधि के साथ टाइप पैरामीटर का उपयोग करने की आवश्यकता है। आप इसे लिख सकते हैं:

static <E> void doIt(E object) 

3

निम्नलिखित की तरह कुछ आप के करीब हो जाएगा

class Clazz
{
   public static <U extends Clazz> void doIt(U thing)
   {
   }
}

EDIT: अधिक विस्तार के साथ अद्यतित उदाहरण

public abstract class Thingo 
{

    public static <U extends Thingo> void doIt(U p_thingo)
    {
        p_thingo.thing();
    }

    protected abstract void thing();

}

class SubThingoOne extends Thingo
{
    @Override
    protected void thing() 
    {
        System.out.println("SubThingoOne");
    }
}

class SubThingoTwo extends Thingo
{

    @Override
    protected void thing() 
    {
        System.out.println("SuThingoTwo");
    }

}

public class ThingoTest 
{

    @Test
    public void test() 
    {
        Thingo t1 = new SubThingoOne();
        Thingo t2 = new SubThingoTwo();

        Thingo.doIt(t1);
        Thingo.doIt(t2);

        // compile error -->  Thingo.doIt(new Object());
    }
}

बिल्कुल जेसन एस ने सुझाव दिया।
आंद्रे चैलेला

@ पिछले सुझाव में यह प्रतिबंध नहीं दिया गया था कि U को Clazz का विस्तार करना होगा
ejj

मैं देखता हूं, लेकिन यह बाधा के अलावा कुछ भी उपयोगी नहीं जोड़ता है। अपने मूल पोस्ट में, मैं एक पैरामीटर के रूप में यू पास किए बिना ऐसा करने में सक्षम होना चाहूंगा।
आंद्रे चैलेला

@ और मेरे अपडेट को ऊपर देखें। जेनेरिक प्रकार केवल विधि परिभाषा में परिभाषित किया गया है, और विधि को कॉल करते समय पारित करने की आवश्यकता नहीं है
ekj

@ekj धन्यवाद, मैं अब समझ गया हूं। क्षमा करें, मैंने यह प्रश्न तीन साल पहले रखा था, जब मैं जावा दैनिक कर रहा था। मुझे आपका विचार मिला, हालांकि मुझे लगता है कि आप समझते हैं कि यह वही नहीं है जो मैंने मूल रूप से इरादा किया था। हालाँकि, मुझे आपका उत्तर पसंद आया।
एंड्रे शैले

2

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

जेनरिक C ++ में टेम्प्लेट की तरह काम करते हैं, इसलिए आपको पहले अपनी कक्षा को तुरंत करना चाहिए, फिर निर्दिष्ट प्रकार के साथ फ़ंक्शन का उपयोग करें।


2
जावा जेनेरिक C ++ टेम्प्लेट से काफी अलग हैं। सामान्य वर्ग स्वयं द्वारा संकलित है। वास्तव में C ++ में समतुल्य कोड काम करेगा (कॉलिंग कोड जैसा दिखेगा Clazz<int>::doIt( 5 ))
डेविड रॉड्रिग्ज़ - dribeas

मुझे यह उत्तर स्वीकार किए गए से बेहतर लगता है ... सी ++ टेम्पलेट संदर्भ के अलावा, सामान्य प्रकार के बारे में टिप्पणी केवल पूरी कक्षा के बजाय कक्षा के एक उदाहरण के लिए है। स्वीकृत उत्तर इस बात की व्याख्या नहीं करता है, और बस एक वर्कअराउंड प्रदान करता है जिसका कुछ भी लेना-देना नहीं है कि आप क्लास के जेनेरिक प्रकार का स्थैतिक तरीके से उपयोग क्यों नहीं कर सकते हैं।
Jorn

1

इसे सरल शब्दों में कहें, तो यह जेनरिक की "एरासुर" संपत्ति के कारण होता है। इसका मतलब है कि हालांकि हम परिभाषित करते हैं ArrayList<Integer>और ArrayList<String>, संकलन के समय यह दो अलग-अलग ठोस प्रकारों के रूप में रहता है, लेकिन रनवे पर जेवीएम सामान्य प्रकार और दो वर्गों के बजाय केवल एक ArrayList वर्ग बनाता है। तो जब हम एक सामान्य के लिए एक स्थिर प्रकार विधि या कुछ भी परिभाषित करते हैं, यह है कि जेनेरिक के सभी उदाहरणों द्वारा साझा किया जाता है, मेरे उदाहरण में यह दोनों द्वारा साझा किया जाता ArrayList<Integer>है और ArrayList<String>.कि के कारण है कि आप एक वर्ग है नहीं की error.A जेनेरिक प्रकार पैरामीटर प्राप्त एक स्थिर संदर्भ में अनुमति है!


1

@ बीडी ऑन रिवेनहिल: चूंकि इस पुराने सवाल ने पिछले साल नए सिरे से ध्यान आकर्षित किया है, इसलिए हमें चर्चा के लिए, थोड़ा आगे बढ़ना चाहिए। आपकी doItविधि का शरीर कुछ भी नहीं करता है, बिल्कुल भी नहीं T। यह रहा:

public class Clazz<T> {
  static <T> void doIt(T object) {
    System.out.println("shake that booty '" + object.getClass().toString()
                       + "' !!!");
  }
// ...
}

तो आप पूरी तरह से सभी प्रकार के चर और सिर्फ कोड को छोड़ सकते हैं

public class Clazz {
  static void doIt(Object object) {
    System.out.println("shake that booty '" + object.getClass().toString()
                       + "' !!!");
  }
// ...
}

ठीक। लेकिन मूल समस्या के करीब आते हैं। वर्ग घोषणा पर पहला प्रकार चर निरर्थक है। केवल विधि पर दूसरे एक की जरूरत है। यहां हम फिर से जाते हैं, लेकिन यह अंतिम जवाब नहीं है, फिर भी:

public class Clazz  {
  static <T extends Saying> void doIt(T object) {
    System.out.println("shake that booty "+ object.say());
  }

  public static void main(String args[]) {
    Clazz.doIt(new KC());
    Clazz.doIt(new SunshineBand());
  }
}
// Output:
// KC
// Sunshine

interface Saying {
      public String say();
}

class KC implements Saying {
      public String say() {
          return "KC";
      }
}

class SunshineBand implements Saying {
      public String say() {
          return "Sunshine";
      }
}

हालाँकि, यह सभी कुछ भी नहीं के बारे में बहुत अधिक उपद्रव है, चूंकि निम्न संस्करण उसी तरह काम करता है। यह सभी की जरूरत है विधि पैरामीटर पर इंटरफ़ेस प्रकार है। कहीं भी देखने में कोई प्रकार चर नहीं। क्या वास्तव में यह मूल समस्या थी?

public class Clazz  {
  static void doIt(Saying object) {
    System.out.println("shake that booty "+ object.say());
  }

  public static void main(String args[]) {
    Clazz.doIt(new KC());
    Clazz.doIt(new SunshineBand());
  }
}

interface Saying {
      public String say();
}

class KC implements Saying {
      public String say() {
          return "KC";
      }
}

class SunshineBand implements Saying {
      public String say() {
          return "Sunshine";
      }
}

0

चूंकि स्थिर चर वर्ग के सभी उदाहरणों द्वारा साझा किए जाते हैं। उदाहरण के लिए यदि आप निम्नलिखित कोड हैं

class Class<T> {
  static void doIt(T object) {
    // using T here 
  }
}

T केवल एक उदाहरण के उपलब्ध होने के बाद उपलब्ध है। लेकिन उदाहरण उपलब्ध होने से पहले ही स्थैतिक तरीकों का उपयोग किया जा सकता है। इसलिए, जेनेरिक प्रकार के मापदंडों को स्थिर तरीकों और चर के अंदर संदर्भित नहीं किया जा सकता है

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