2009-07-01 7 views
30

This question has been asked in a C++ context लेकिन मैं जावा के बारे में उत्सुक हूं। आभासी तरीकों के बारे में चिंताओं लागू नहीं है (मुझे लगता है कि), लेकिन अगर आप इस स्थिति है:विधि श्रृंखला + विरासत एक साथ अच्छी तरह से खेल नहीं है?

abstract class Pet 
{ 
    private String name; 
    public Pet setName(String name) { this.name = name; return this; }   
} 

class Cat extends Pet 
{ 
    public Cat catchMice() { 
     System.out.println("I caught a mouse!"); 
     return this; 
    } 
} 

class Dog extends Pet 
{ 
    public Dog catchFrisbee() { 
     System.out.println("I caught a frisbee!"); 
     return this; 
    } 
} 

class Bird extends Pet 
{ 
    public Bird layEgg() { 
     ... 
     return this; 
    } 
} 


{ 
    Cat c = new Cat(); 
    c.setName("Morris").catchMice(); // error! setName returns Pet, not Cat 
    Dog d = new Dog(); 
    d.setName("Snoopy").catchFrisbee(); // error! setName returns Pet, not Dog 
    Bird b = new Bird(); 
    b.setName("Tweety").layEgg(); // error! setName returns Pet, not Bird 
} 

वर्ग पदानुक्रम की इस तरह, वहाँ एक तरीका है कि नहीं करता है में this वापस जाने के लिए (किसी भी तरह से है प्रभावी ढंग से) ऑब्जेक्ट प्रकार को अपस्टास्ट करें?

+4

अब मैं समझता हूँ क्यों इतने सारे लोग जावा से नफरत है। –

उत्तर

47

आप अपने संकलक से अनियंत्रित डाली चेतावनी से बचना चाहते हैं (और @SuppressWarnings ("अनियंत्रित" नहीं करना चाहती)) है, तो आप एक करने की ज़रूरत है छोटे से अधिक:

सबसे पहले, पालतू पशु की अपनी परिभाषा, आत्म संदर्भ होना चाहिए पालतू हमेशा एक सामान्य प्रकार है क्योंकि:

abstract class Pet <T extends Pet<T>> 

दूसरा, (T) this सेटनाम में कास्ट भी अनचेक किया गया है। इससे बचने के लिए, उत्कृष्ट Generics FAQ by Angelika Langer में "getThis" तकनीक का उपयोग करें:

"getThis" चाल इस संदर्भ का सही प्रकार की वसूली के लिए एक रास्ता प्रदान करता है।

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

जिसके परिणामस्वरूप कोड है:

public class TestClass { 

    static abstract class Pet <T extends Pet<T>> { 
    private String name; 

    protected abstract T getThis(); 

    public T setName(String name) { 
     this.name = name; 
     return getThis(); } 
    } 

    static class Cat extends Pet<Cat> { 
    @Override protected Cat getThis() { return this; } 

    public Cat catchMice() { 
     System.out.println("I caught a mouse!"); 
     return getThis(); 
    } 
    } 

    static class Dog extends Pet<Dog> { 
    @Override protected Dog getThis() { return this; } 

    public Dog catchFrisbee() { 
     System.out.println("I caught a frisbee!"); 
     return getThis(); 
    } 
    } 

    public static void main(String[] args) { 
    Cat c = new Cat(); 
    c.setName("Morris").catchMice(); 
    Dog d = new Dog(); 
    d.setName("Snoopy").catchFrisbee(); 
    } 
} 
+0

कोड इस तरह से क्लीनर हो जाता है, और मुझे पूर्ण एंजेलिका लेख पढ़ने के लिए कुछ समय लगेगा, thx vm! –

+2

'वर्ग सांप पालतू {@ ओवरराइड संरक्षित बिल्ली getThis() {वापसी नई बिल्ली();}} ' – immibis

+1

यह थोड़ा मुश्किल हो जाता है हालांकि आपके पास मध्य में होने वाली गैर-सारणी, गैर-अंतिम कक्षाएं होती हैं और आवश्यकता होती है एक उदाहरण बनाने के लिए। उदाहरण के लिए, मान लीजिए कि आपके पास 'स्थैतिक वर्ग पूडल कुत्ता ' बढ़ाता है, और 'कुत्ते' को 'स्थिर वर्ग कुत्ते' के रूप में बदलता है <टी कुत्ते बढ़ाता है> पालतू 'बढ़ाता है। अब 'कुत्ते' का कच्चा उदाहरण बनाना मुश्किल होगा। – Marcus

9

नहीं, वास्तव में नहीं। (, Covariant वापसी प्रकार केवल जावा 5 में और ऊपर हैं कि यदि आप के लिए एक चिंता का विषय है।)

@Override 
public Cat setName(String name) { 
    super.setName(name); 
    return this; 
} 

16
: आप covariant वापसी प्रकार (सही नाम के लिए मैकडोवेल के लिए धन्यवाद) का उपयोग करके उसके चारों ओर काम कर सकता था

कैसे इस पुरानी चाल के बारे में:

abstract class Pet<T extends Pet> 
{ 
    private String name; 
    public T setName(String name) { this.name = name; return (T) this; }   
} 

class Cat extends Pet<Cat> 
{ 
    /* ... */ 
} 

class Dog extends Pet<Dog> 
{ 
    /* ... */ 
} 
+0

+1, मैंने जो किया उससे अधिक संक्षेप में व्यक्त किया। लेकिन यह देखते हुए कि जावा जेनेरिक कितने समय तक रहे हैं, यह कितनी पुरानी चाल हो सकती है? –

+0

आह, मुझे लगा कि जेनेरिक के साथ कुछ होगा, बस नहीं पता था कि क्या। धन्यवाद! –

+0

@ स्टेव बी: यह जावा में पुराना नहीं है (असल में, मुझे नहीं लगता कि मैंने इसे जावा में इस्तेमाल किया है), लेकिन इसका उपयोग लंबे समय से सी ++ में किया गया है। –

4

यह थोड़ा जटिल है, लेकिन आप जेनरिक के साथ ऐसा कर सकते हैं:

abstract class Pet< T extends Pet > { 
    private String name; 

    public T setName(String name) { 
     this.name = name; 
     return (T)this; 
    } 

    public static class Cat extends Pet<Cat> { 
     public Cat catchMice() { 
      System.out.println("I caught a mouse!"); 
      return this; 
     } 
    } 

    public static class Dog extends Pet<Dog> { 
     public Dog catchFrisbee() { 
      System.out.println("I caught a frisbee!"); 
      return this; 
     } 
    } 

    public static void main (String[] args){ 
     Cat c = new Cat(); 
     c.setName("Morris").catchMice(); // error! setName returns Pet, not Cat 
     Dog d = new Dog(); 
     d.setName("Snoopy").catchFrisbee(); // error! setName returns Pet, not Dog 
    } 

} 
2
public class Pet<AnimalType extends Pet> { 

private String name; 
    public AnimalType setName(String name) { 
     this.name = name; return (AnimalType)this; 
    }   
} 

और

public class Cat extends Pet<Cat> { 

    public Cat catchMice() {return this;} 

    public static void main(String[] args) { 
     Cat c = new Cat().setName("bob").catchMice(); 
    } 

}

+0

@ स्टेव बी - +1, मुझे यह शर्त लगाओ! –

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