अपने त्रुटि की कुंजी के प्रकार के जेनेरिक घोषणा में है F
: F extends Function<T, R>
। काम नहीं करने वाला कथन है: new Builder<MyInterface>().with(MyInterface::getNumber, 4L);
पहला, आपके पास एक नया है Builder<MyInterface>
। इसलिए वर्ग की घोषणा का अर्थ है T = MyInterface
। आपकी घोषणा के अनुसार with
, F
एक होना चाहिए Function<T, R>
, जो Function<MyInterface, R>
इस स्थिति में है। इसलिए, पैरामीटर getter
एक रखना चाहिए MyInterface
पैरामीटर (विधि संदर्भ से संतुष्ट के रूप में MyInterface::getNumber
और MyInterface::getLong
), और बदले R
, जो कार्य करने के लिए दूसरा पैरामीटर के रूप में एक ही प्रकार का होना चाहिए with
। अब, देखते हैं कि क्या यह आपके सभी मामलों के लिए है:
// T = MyInterface, F = Function<MyInterface, Long>, R = Long
new Builder<MyInterface>().with(MyInterface::getLong, 4L);
// T = MyInterface, F = Function<MyInterface, Number>, R = Number
// 4L explicitly widened to Number
new Builder<MyInterface>().with(MyInterface::getNumber, (Number) 4L);
// T = MyInterface, F = Function<MyInterface, Number>, R = Number
// 4L implicitly widened to Number
new Builder<MyInterface>().<Function<MyInterface, Number>, Number>with(MyInterface::getNumber, 4L);
// T = MyInterface, F = Function<MyInterface, Number>, R = Number
// 4L implicitly widened to Number
new Builder<MyInterface>().with((Function<MyInterface, Number>) MyInterface::getNumber, 4L);
// T = MyInterface, F = Function<MyInterface, Number>, R = Long
// F = Function<T, not R> violates definition, therefore compilation error occurs
// Compiler cannot infer type of method reference and 4L at the same time,
// so it keeps the type of 4L as Long and attempts to infer a match for MyInterface::getNumber,
// only to find that the types don't match up
new Builder<MyInterface>().with(MyInterface::getNumber, 4L);
आप निम्न विकल्पों के साथ इस समस्या को "ठीक" कर सकते हैं:
// stick to Long
new Builder<MyInterface>().with(MyInterface::getLong, 4L);
// stick to Number
new Builder<MyInterface>().with(MyInterface::getNumber, (Number) 4L);
// explicitly convert the result of getNumber:
new Builder<MyInterface>().with(myInstance -> (Long) myInstance.getNumber(), 4L);
// explicitly convert the result of getLong:
new Builder<MyInterface>().with(myInterface -> (Number) myInterface.getLong(), (Number) 4L);
इस बिंदु से परे, यह ज्यादातर एक डिजाइन निर्णय है जिसके लिए विकल्प आपके विशेष एप्लिकेशन के लिए कोड जटिलता को कम करता है, इसलिए जो भी आपको सबसे अच्छा लगता है उसे चुनें।
जावा भाषा विनिर्देश से निम्नलिखित कारण यह हो सकता है कि आप ऐसा नहीं कर सकते हैं :
बॉक्सिंग रूपांतरण एक आदिम प्रकार के भावों को संबंधित संदर्भ प्रकार के अभिव्यक्तियों के रूप में मानता है। विशेष रूप से, निम्नलिखित नौ रूपांतरणों को बॉक्सिंग रूपांतरण कहा जाता है :
- टाइप बूलियन से टाइप बूलियन
- टाइप बाइट से टाइप बाइट तक
- टाइप शॉर्ट से टाइप शॉर्ट तक
- टाइप चार से टाइप कैरेक्टर
- टाइप इंट से टाइप इंटेगर
- टाइप लॉन्ग से टाइप लॉन्ग
- फ्लोट टाइप से फ्लोट टाइप करें
- टाइप डबल से टाइप डबल
- अशक्त प्रकार से अशक्त प्रकार तक
जैसा कि आप स्पष्ट रूप से देख सकते हैं कि लंबे समय से नंबर तक कोई निहित बॉक्सिंग रूपांतरण नहीं है, और लॉन्ग से नंबर तक का व्यापक रूपांतरण केवल तब हो सकता है जब कंपाइलर को यह सुनिश्चित हो कि उसे नंबर की आवश्यकता है और लॉन्ग की नहीं। जैसा कि विधि संदर्भ के बीच एक संघर्ष होता है जिसके लिए एक नंबर और 4L की आवश्यकता होती है जो लॉजिकल प्रदान करता है (किसी कारण से ???) तार्किक छलांग लगाने में असमर्थ है कि लॉन्ग एक संख्या है और यह घटाता है कि F
ए Function<MyInterface, Number>
।
इसके बजाय, मैं फ़ंक्शन हस्ताक्षर को थोड़ा संपादित करके समस्या को हल करने में कामयाब रहा:
public <R> Builder<T> with(Function<T, ? super R> getter, R returnValue) {
return null;//TODO
}
इस परिवर्तन के बाद, निम्नलिखित होता है:
// doesn't work, as it should not work
new Builder<MyInterface>().with(MyInterface::getLong, (Number), 4L);
// works, as it always did
new Builder<MyInterface>().with(MyInterface::getLong, 4L);
// works, as it should work
new Builder<MyInterface>().with(MyInterface::getNumber, (Number)4L);
// works, as you wanted
new Builder<MyInterface>().with(MyInterface::getNumber, 4L);
संपादित करें:
इस पर कुछ और समय बिताने के बाद, गेट्टर-आधारित प्रकार की सुरक्षा को लागू करना मुश्किल है। यहां एक कार्यशील उदाहरण है जो बिल्डर के प्रकार-सुरक्षा को लागू करने के लिए सेटर विधियों का उपयोग करता है:
public class Builder<T> {
static public interface MyInterface {
//setters
void number(Number number);
void Long(Long Long);
void string(String string);
//getters
Number number();
Long Long();
String string();
}
// whatever object we're building, let's say it's just a MyInterface for now...
private T buildee = (T) new MyInterface() {
private String string;
private Long Long;
private Number number;
public void number(Number number)
{
this.number = number;
}
public void Long(Long Long)
{
this.Long = Long;
}
public void string(String string)
{
this.string = string;
}
public Number number()
{
return this.number;
}
public Long Long()
{
return this.Long;
}
public String string()
{
return this.string;
}
};
public <R> Builder<T> with(BiConsumer<T, R> setter, R val)
{
setter.accept(this.buildee, val); // take the buildee, and set the appropriate value
return this;
}
public static void main(String[] args) {
// works:
new Builder<MyInterface>().with(MyInterface::Long, 4L);
// works:
new Builder<MyInterface>().with(MyInterface::number, (Number) 4L);
// compile time error, as it shouldn't work
new Builder<MyInterface>().with(MyInterface::Long, (Number) 4L);
// works, as it always did
new Builder<MyInterface>().with(MyInterface::Long, 4L);
// works, as it should
new Builder<MyInterface>().with(MyInterface::number, (Number)4L);
// works, as you wanted
new Builder<MyInterface>().with(MyInterface::number, 4L);
// compile time error, as you wanted
new Builder<MyInterface>().with(MyInterface::number, "blah");
}
}
ऑब्जेक्ट के निर्माण की सुरक्षित-सुरक्षित क्षमता प्रदान की है, उम्मीद है कि भविष्य में किसी बिंदु पर हम बिल्डर से एक अपरिवर्तनीय डेटा ऑब्जेक्ट को वापस करने में सक्षम होंगे (हो सकता है toRecord()
कि इंटरफ़ेस में एक विधि जोड़कर , और बिल्डर को निर्दिष्ट करते हुए Builder<IntermediaryInterfaceType, RecordType>
), इसलिए आपको परिणामी वस्तु के संशोधित होने की भी चिंता नहीं करनी चाहिए। ईमानदारी से, यह एक पूरी तरह से शर्म की बात है कि इसे एक प्रकार का सुरक्षित क्षेत्र-लचीला बिल्डर प्राप्त करने के लिए बहुत प्रयास की आवश्यकता है, लेकिन कुछ नई सुविधाओं, कोड पीढ़ी या प्रतिबिंब की कष्टप्रद राशि के बिना यह असंभव है।
MyInterface
?