2015-09-02 8 views
5

यह जावाएफएक्स प्रश्न नहीं है, लेकिन मैं JavaFX में एक इंटरफ़ेस लिखने की कोशिश कर रहा हूं जो एक वर्ग को देखने योग्य घोषित करता है। देखने योग्य वर्गों का अर्थ एक दृश्य() विधि है जो उस दृश्यमान का प्रतिनिधित्व करने वाले नोड ऑब्जेक्ट को लौटाता है। अभी तक सरल, लेकिन यहां यह जटिल हो जाता है। लौटाए गए नोड को एक प्राप्त करने योग्य() विधि की गारंटी दी जानी चाहिए जो देखने योग्य ऑब्जेक्ट को दर्शाती है। मैं इसे कैसे पूर्ण करूं? मेरी पहली वृत्ति कुछ इस तरह की कोशिश करने का था:जावा रिटर्न टाइप में एकाधिक जेनेरिक बाउंड लागू करना

interface Viewable<V extends Viewable<V>>{ 
    <N extends Node&View<V>>N view(); 
} 
interface View<V extends Viewable<V>>{ 
    V getViewable(); 
} 

पहली बार में कौन सा ध्वनि प्रकट होता है, और इस तरह के निम्नलिखित वर्गों की अनुमति देता है:

class ViewableObject implements Viewable<ViewableObject>{ 
    @Override public ObjectView view(){ 
     return new ObjectView(); 
    } 
    class ObjectView extends Pane implements View<ViewableObject>{ 
     @Override public ViewableObject getViewable(){ 
      return ViewableObject.this; 
     } 
    } 
} 

हालांकि, किसी कारण से, इस वर्ग के भी संकलित:

class ViewableObject implements Viewable<ViewableObject>{ 
    @Override public Pane view(){ 
     return new Pane(); 
    } 
} 

फलक एक नोड है, लेकिन यह दृश्य लागू नहीं करता है, तो यह वर्ग संकलित क्यों करता है? मुझे लगता है कि यह दृश्य() विधि के अनुबंध का उल्लंघन कर रहा है।

class ViewableObject implements Viewable<ViewableObject>{ 
    @Override public Object view(){//complains this is not an @Override 
     return new Object(); 
    } 
} 

यहाँ क्या हो रहा है: यहां तक ​​कि अजनबी, एक ही कक्षा जब फलक वस्तु साथ बदल दिया है संकलन करने में विफल रहता है? क्या जेनेरिक की मेरी समझ में कोई दोष है? मैं इसे इरादे के रूप में काम करने के लिए कैसे प्राप्त कर सकता हूं?

+0

सभी प्री-जेनेरिक कोड के साथ पिछड़े संगतता के नाम पर। लेकिन [spec] के मेरे पढ़ने (http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.4।5) संकलक के साथ असहमत लगता है। किसी भी मामले में, यह सुरक्षित हो सकता है, बशर्ते कि प्रोग्रामर टाइप सिस्टम में व्यक्त न किए गए बैंड अनुबंधों का पालन करता हो। इस मामले में, फलक का उप-प्रकार लौटा रहा है जो दृश्य लागू करता है – ZhongYu

उत्तर

2

आप इस मामले में एक सामान्य विधि का उपयोग नहीं करना चाहते हैं, क्योंकि आपका लक्ष्य व्यू() के रिटर्न वैल्यू को ठीक करना है। एक सामान्य विधि कॉलर ठोस प्रकार निर्धारित करने देती है। तो वास्तव में आप लागू करने के ठीक विपरीत कर रहे हैं।

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

interface Viewable<V extends Viewable<V, N>, N extends Node & View<V, N>> { 
    N view(); 
} 

interface View<V extends Viewable<V, TNode>, TNode extends Node & View<V, TNode>> { 
    V getViewable(); 
} 

class ViewableObject implements Viewable<ViewableObject, ViewableObject.ObjectView> { 
    @Override 
    public ObjectView view() { 
     return new ObjectView(); 
    } 

    class ObjectView extends Pane implements View<ViewableObject, ObjectView> { 
     @Override 
     public ViewableObject getViewable() { 
      return ViewableObject.this; 
     } 
    } 
} 

आप Viewable.view() की घोषणा के लिए बाइट कोड पर एक नज़र डालें, तो आप उस संकलक के रूप में निर्दिष्ट करने के लिए बाध्य पहले प्रकार का चयन करता है देखेंगे विधि के लिए वास्तविक वापसी प्रकार। यहाँ इंटेलीजे बाइट कोड दर्शक से उत्पादन के प्रासंगिक पंक्तियां हैं:

// declaration: N view<N extends org.cumberlw.viewtest.Node, org.cumberlw.viewtest.View<V>>() 
public abstract view()Lorg/cumberlw/viewtest/Node; 

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

// declaration: N view<N extends org.cumberlw.viewtest.View<V>, org.cumberlw.viewtest.Node>() 
public abstract view()Lorg/cumberlw/viewtest/View; 

सूचना है कि वापसी मान अब देखें है। तो अब आपका दूसरा उदाहरण संकलित नहीं होगा क्योंकि फलक दृश्य का उप-वर्ग नहीं है। पैरामीटर का कोई भी क्रम तीसरा उदाहरण संकलित नहीं करेगा क्योंकि ऑब्जेक्ट नोड या व्यू का उप-वर्ग नहीं है।


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

interface DogLike { 
    void bark(); 
} 

interface CatLike { 
    void meow(); 
} 

class Dog implements DogLike { 
    @Override 
    public void bark() { 
     System.out.println("Woof"); 
    } 
} 

interface MoreauMachine { 
    <H extends DogLike & CatLike > H createHybrid(); 
} 

class MalfunctioningDogCatFactory implements MoreauMachine { 

    @Override 
    public DogLike createHybrid() { 
     //Compile with -Xlint:unchecked to see a warning here: 
     //Warning:(84, 20) java: createHybrid() in org.cumberlw.viewtest.MalfunctioningDogCatFactory implements <H>createHybrid() in org.cumberlw.viewtest.MoreauMachine 
     //return type requires unchecked conversion from org.cumberlw.viewtest.DogLike to H 
     return new Dog(); 
    } 

    public static void main(String[] args) { 
     MoreauMachine factory = new MalfunctioningDogCatFactory(); 

     //crashes! 
     //Exception in thread "main" java.lang.ClassCastException: org.cumberlw.viewtest.Dog cannot be cast to org.cumberlw.viewtest.CatLike 
     factory.createHybrid().meow(); 
    } 
} 
संबंधित मुद्दे