2013-01-23 7 views
6

मैं कुछ विरासत जावा कोड है कि मेरे नियंत्रण के बाहर कहीं एक सामान्य payload चर को परिभाषित करता है है (यानी मैं अपने प्रकार नहीं बदल सकते):मिक्सिंग स्कैला और जावा: सामान्य रूप से टाइप किए गए कन्स्ट्रक्टर पैरामीटर को कैसे प्राप्त करें?

// Java code 
Wrapper<? extends SomeBaseType> payload = ... 

मैं एक विधि पैरामीटर के रूप में इस तरह के एक payload मूल्य प्राप्त मेरे कोड में और इसे स्कैला case class (एक अभिनेता प्रणाली के साथ संदेश के रूप में उपयोग करने के लिए) पर पास करना चाहते हैं, लेकिन परिभाषाएं सही नहीं हैं कि मुझे कम से कम एक कंपाइलर चेतावनी नहीं मिलती है।

// still Java code 
ScalaMessage msg = new ScalaMessage(payload); 

यह एक संकलक चेतावनी देता है "प्रकार की सुरक्षा: contructor ... कच्चे प्रकार के अंतर्गत आता है ..."

स्काला case class के रूप में परिभाषित किया गया है:

// Scala code 
case class ScalaMessage[T <: SomeBaseType](payload: Wrapper[T]) 

मैं कैसे परिभाषित कर सकते हैं केस क्लास जैसे कोड स्पष्ट रूप से संकलित करता है?

अपडेट किया गया पेलोड पैरामीटर

के मूल स्पष्ट करने के लिए जोड़ा गया तुलना के लिए (दुर्भाग्य से, जावा Wrapper वर्ग के कोड या payload पैरामीटर का प्रकार बदलने एक विकल्प नहीं है), जावा में मैं सिर्फ एक ही तरह से एक पैरामीटर के रूप में परिभाषित कर सकते हैं payload चर परिभाषित किया गया है:

// Java code 
void doSomethingWith(Wrapper<? extends SomeBaseType> payload) {} 

और यह acco फोन rdly

// Java code 
doSomethingWith(payload) 

लेकिन मैं तत्काल नहीं कर सकता "कच्चे प्रकार" चेतावनी के बिना सीधे एक रैपर ऑब्जेक्ट।

static <T> Wrapper<T> of(T value) { 
    return new Wrapper<T>(value); 
} 

और इस स्थिर सहायक का प्रयोग कर एक Wrapper वस्तु का दृष्टांत को:

// Java code 
MyDerivedType value = ... // constructed elsewhere, actual type is not known! 
Wrapper<? extends SomeBaseType> payload = Wrapper.of(value); 

समाधान

मैं एक ऐसी ही सहायक विधि जोड़ सकते हैं यहाँ, मैं एक static सहायक विधि का उपयोग करने की आवश्यकता है एक स्कैला साथी वस्तु के लिए:

// Scala code 
object ScalaMessageHelper { 
    def apply[T <: SomeBaseType](payload: Wrapper[T]) = 
     new ScalaMessage(payload) 
} 
object ScalaMessageHelper2 { 
    def apply[T <: SomeBaseType](payload: Wrapper[T]) = 
     ScalaMessage(payload) // uses implicit apply() method of case class 
} 

और ScalaMessage वर्ग w/ओ समस्याओं का दृष्टांत को जावा से इस का उपयोग करें:

// Java code 
ScalaMessage msg = ScalaMessageHelper.apply(payload); 

जब तक किसी को एक और अधिक सुरुचिपूर्ण समाधान के साथ आता है, मैं इस एक जवाब के रूप निकाल देंगे ...

धन्यवाद !

उत्तर

3

मुझे लगता है कि समस्या यह है कि जावा में आप निम्न कर, यदि:

ScalaMessage msg = new ScalaMessage(payload); 

तो फिर तुम ScalaMessage अपने कच्चे प्रकार का उपयोग कर instantiating कर रहे हैं। या दूसरे शब्दों में, आप गैर-सामान्य प्रकार के रूप में ScalaMessage का उपयोग करते हैं (जब जावा ने जेनेरिक पेश किए, तो उन्होंने एक सामान्य वर्ग को गैर-जेनेरिक के रूप में इलाज करने की क्षमता रखी, ज्यादातर पिछड़े संगतता के लिए)।

आप बस प्रकार पैरामीटर जब ScalaMessage instantiating निर्दिष्ट करना चाहिए:

// (here T = MyDerivedType, where MyDerivedType must extend SomeBaseType 
ScalaMessage<MyDerivedType> msg = new ScalaMessage<>(payload); 

अद्यतन: अपनी टिप्पणी देखने के बाद, मैं वास्तव में यह एक डमी परियोजना में करने की कोशिश की है, और मैं वास्तव में कोई त्रुटि मिलती है:

[error] C:\Code\sandbox\src\main\java\bla\Test.java:8: cannot find symbol 
[error] symbol : constructor ScalaMessage(bla.Wrapper<capture#64 of ? extends bla.SomeBaseType>) 
[error] location: class test.ScalaMessage<bla.SomeBaseType> 
[error]  ScalaMessage<SomeBaseType> msg = new ScalaMessage<SomeBaseType>(payload); 

यह जावा जेनेरिक के बीच एक विसंगति जैसा लगता है (जिसे हम स्कैला में निकास के माध्यम से अनुकरण कर सकते हैं) और स्कैला जेनेरिक। तुम बस ScalaMessage में टाइप पैरामीटर निकाल देता है और इसके बजाय existentials का उपयोग करके इसे ठीक कर सकते हैं:

case class ScalaMessage(payload: Wrapper[_ <: SomeBaseType]) 

और उसके बाद इस तरह जावा में यह दृष्टांत:

new ScalaMessage(payload) 

यह काम करता है। हालांकि, अब ScalaMessage अब सामान्य नहीं है, यदि आप इसे अधिक परिष्कृत पेलोड्स के साथ उपयोग करना चाहते हैं तो एक समस्या हो सकती है (Wrapper<? extends MyDerivedType> कहें)। जावा में तो

case class ScalaMessage[T<:SomeBaseType](payload: Wrapper[_ <: T]) 

और::

इसे ठीक करने के के ScalaMessage को अभी तक एक और छोटा सा परिवर्तन करते हैं

ScalaMessage<SomeBaseType> msg = new ScalaMessage<SomeBaseType>(payload); 

समस्या हल :)

+0

काम नहीं करेगा क्योंकि मेरे पास वास्तव में 'रैपर <प्रकार' का विधि पैरामीटर है? कुछबेस टाइप टाइप>> जावा में जो कोड के ब्लॉक में पास हो गया है, हम चर्चा कर रहे हैं। मैं तदनुसार मूल प्रश्न अपडेट करूंगा। –

+0

मैंने एक अद्यतन किया, इसे जांचें। जावा/स्कैला प्रकार सिस्टम के विस्तृत स्पष्टीकरण के लिए –

1

क्या आप अनुभव कर रहे है तथ्य यह है कि जावा जेनरिक खराब तरीके से कार्यान्वित किए जाते हैं। आप Java में covariance और contravariance को सही ढंग से कार्यान्वित नहीं कर सकते हैं और आपको वाइल्डकार्ड का उपयोग करना होगा।

case class ScalaMessage[T <: SomeBaseType](payload: Wrapper[T]) 

आप एक Wrapper[T] प्रदान करते हैं, इस सही ढंग से काम करेंगे और आप एक ScalaMessage[T]

का एक उदाहरण आप करना चाहते हैं क्या बना देंगे एक Wrapper[K] जहां K<:T से एक ScalaMessage[T] बनाने के लिए सक्षम होने के लिए है अज्ञात है। हालांकि, यह संभव है अगर

Wrapper[K]<:Wrapper[T] for K<:T 

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

class ScalaMessage[T](wrapper:Wrapper[_<:T]) 

आवरण सही ढंग से स्काला में लागू किया गया था यदि फिर भी का उपयोग कर प्रकार विचरण

class Wrapper[+T] 
class ScalaMessage[+T](wrapper:Wrapper[T]) 

object ScalaMessage { 
    class A 
    class B extends A 

    val myVal:Wrapper[_<:A] = new Wrapper[B]() 

    val message:ScalaMessage[A] = new ScalaMessage[A](myVal) 
} 

सब कुछ सुचारू रूप से और सुंदर ढंग से संकलित कर देगा :)

+0

+1 –

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