आपूर्तिकर्ता केवल नो-आर्ग कंस्ट्रक्टर का समर्थन क्यों करते हैं?
यदि डिफ़ॉल्ट कंस्ट्रक्टर मौजूद है, तो मैं यह कर सकता हूं:
create(Foo::new)
लेकिन अगर एकमात्र निर्माता स्ट्रिंग लेता है, तो मुझे यह करना होगा:
create(() -> new Foo("hello"))
आपूर्तिकर्ता केवल नो-आर्ग कंस्ट्रक्टर का समर्थन क्यों करते हैं?
यदि डिफ़ॉल्ट कंस्ट्रक्टर मौजूद है, तो मैं यह कर सकता हूं:
create(Foo::new)
लेकिन अगर एकमात्र निर्माता स्ट्रिंग लेता है, तो मुझे यह करना होगा:
create(() -> new Foo("hello"))
Supplier
करता है, अर्थात एक लंबोदर अभिव्यक्ति का उपयोग करते समय। तो ऐसा लगता है कि आपका वास्तविक सवाल यह है कि "एक विधि संदर्भ केवल तभी काम करता है यदि कार्यात्मक पैरामीटर लक्ष्य मापदंडों से मेल खाते हैं" और उत्तर है, क्योंकि यही विधि संदर्भ हैं। यदि पैरामीटर सूची मेल नहीं खाती है, तो एक लैम्ब्डा अभिव्यक्ति का उपयोग करें जैसा कि आपने पहले ही अपने प्रश्न में दिखाया है। क्योंकि यही लैंबडा एक्सप्रेशन हैं (एक्सक्लूसिव नहीं) ...
जवाबों:
यह विधि संदर्भ सिंटैक्स की सिर्फ एक सीमा है - कि आप किसी भी तर्क में पास नहीं हो सकते। यह सिंटैक्स कैसे काम करता है।
लेकिन, इसके लिए 1-arg कंस्ट्रक्टर T
एक String
संगत है Function<String,T>
:
Function<String, Foo> fooSupplier = Foo::new;
जिस कंस्ट्रक्टर का चयन किया जाता है, उसे लक्ष्य प्रकार के आकार के आधार पर ओवरलोड चयन समस्या के रूप में माना जाता है।
Supplier<T>
इंटरफ़ेस का एक हस्ताक्षर के साथ एक समारोह का प्रतिनिधित्व करता है () -> T
, जिसका अर्थ यह कोई पैरामीटर लेता है और प्रकार के कुछ देता है T
। विधि संदर्भ जो आप तर्क के रूप में प्रदान करते हैं, उस हस्ताक्षर का पालन करना चाहिए ताकि पास किया जा सके।
यदि आप Supplier<Foo>
कंस्ट्रक्टर के साथ काम करना चाहते हैं , तो आप सामान्य बाइंड विधि का उपयोग कर सकते हैं जो @Tagir Valeev से पता चलता है, या आप अधिक विशिष्ट बनाते हैं।
यदि आप चाहते हैं कि एक Supplier<Foo>
हमेशा उस "hello"
स्ट्रिंग का उपयोग करे , तो आप इसे दो अलग-अलग तरीकों से परिभाषित कर सकते हैं: एक विधि या एक Supplier<Foo>
चर के रूप में।
तरीका:
static Foo makeFoo() { return new Foo("hello"); }
परिवर्तनशील:
static Supplier<Foo> makeFoo = () -> new Foo("hello");
आप एक विधि संदर्भ ( create(WhateverClassItIsOn::makeFoo);
) के साथ विधि में पारित कर सकते हैं , और चर को केवल नाम का उपयोग करके पारित किया जा सकता है create(WhateverClassItIsOn.makeFoo);
।
विधि थोड़ा अधिक बेहतर है क्योंकि विधि संदर्भ के रूप में पारित होने के संदर्भ के बाहर का उपयोग करना आसान है, और यह उदाहरण में भी इस्तेमाल किया जा सकता है कि किसी को अपने स्वयं के विशेष कार्यात्मक इंटरफ़ेस की आवश्यकता होती है जो कि () -> T
या () -> Foo
विशेष रूप से है ।
यदि आप Supplier
एक तर्क के रूप में किसी भी स्ट्रिंग का उपयोग करना चाहते हैं , तो आपको कुछ विधि का उपयोग करना चाहिए जैसे @ विधि का उल्लेख किया गया है, आपूर्ति की आवश्यकता को दरकिनार करते हुए Function
:
Supplier<Foo> makeFooFromString(String str) { return () -> new Foo(str); }
आप इसे इस तरह एक तर्क के रूप में पारित कर सकते हैं: create(makeFooFromString("hello"));
हालांकि, शायद आपको "आपूर्ति ..." कॉल करने के लिए सभी "मेक ..." कॉल को बदलना चाहिए, बस इसे थोड़ा स्पष्ट करने के लिए।
आपूर्तिकर्ता केवल बिना-आर्ग निर्माणकर्ताओं के साथ काम क्यों करते हैं?
क्योंकि एक 1-आर्ग निर्माता जैसे 1 तर्क और 1 वापसी मान, के साथ एक सैम इंटरफ़ेस के लिए है isomorphic java.util.function.Function<T,R>
की R apply(T)
।
दूसरी ओर Supplier<T>
के T get()
एक शून्य आर्ग निर्माता isomorphic को है।
वे बस संगत नहीं हैं। या तो आपकी create()
विधि को विभिन्न कार्यात्मक इंटरफेस को स्वीकार करने और अलग-अलग तरीके से कार्य करने के लिए बहुरूपिक होने की आवश्यकता है, जिसके आधार पर तर्क की आपूर्ति की जाती है या आपको दो हस्ताक्षर के बीच गोंद कोड के रूप में कार्य करने के लिए एक लंबो बॉडी लिखना होगा।
यहाँ आपकी क्या उम्मीद है? आपकी राय में क्या होना चाहिए ?
एक कार्यात्मक Interface के साथ आपूर्तिकर्ता जोड़ी।
यहाँ कुछ नमूना कोड है जो मैंने फंक्शन के साथ एक विशिष्ट कंस्ट्रक्टर के लिए एक कंस्ट्रक्टर संदर्भ "बाइंडिंग" को प्रदर्शित करने के लिए रखा है और "फैक्ट्री" कंस्ट्रक्टर संदर्भों को परिभाषित करने और इनवॉइस करने के विभिन्न तरीके भी हैं।
import java.io.Serializable;
import java.util.Date;
import org.junit.Test;
public class FunctionalInterfaceConstructor {
@Test
public void testVarFactory() throws Exception {
DateVar dateVar = makeVar("D", "Date", DateVar::new);
dateVar.setValue(new Date());
System.out.println(dateVar);
DateVar dateTypedVar = makeTypedVar("D", "Date", new Date(), DateVar::new);
System.out.println(dateTypedVar);
TypedVarFactory<Date, DateVar> dateTypedFactory = DateVar::new;
System.out.println(dateTypedFactory.apply("D", "Date", new Date()));
BooleanVar booleanVar = makeVar("B", "Boolean", BooleanVar::new);
booleanVar.setValue(true);
System.out.println(booleanVar);
BooleanVar booleanTypedVar = makeTypedVar("B", "Boolean", true, BooleanVar::new);
System.out.println(booleanTypedVar);
TypedVarFactory<Boolean, BooleanVar> booleanTypedFactory = BooleanVar::new;
System.out.println(booleanTypedFactory.apply("B", "Boolean", true));
}
private <V extends Var<T>, T extends Serializable> V makeVar(final String name, final String displayName,
final VarFactory<V> varFactory) {
V var = varFactory.apply(name, displayName);
return var;
}
private <V extends Var<T>, T extends Serializable> V makeTypedVar(final String name, final String displayName, final T value,
final TypedVarFactory<T, V> varFactory) {
V var = varFactory.apply(name, displayName, value);
return var;
}
@FunctionalInterface
static interface VarFactory<R> {
// Don't need type variables for name and displayName because they are always String
R apply(String name, String displayName);
}
@FunctionalInterface
static interface TypedVarFactory<T extends Serializable, R extends Var<T>> {
R apply(String name, String displayName, T value);
}
static class Var<T extends Serializable> {
private String name;
private String displayName;
private T value;
public Var(final String name, final String displayName) {
this.name = name;
this.displayName = displayName;
}
public Var(final String name, final String displayName, final T value) {
this(name, displayName);
this.value = value;
}
public void setValue(final T value) {
this.value = value;
}
@Override
public String toString() {
return String.format("%s[name=%s, displayName=%s, value=%s]", getClass().getSimpleName(), this.name, this.displayName,
this.value);
}
}
static class DateVar extends Var<Date> {
public DateVar(final String name, final String displayName) {
super(name, displayName);
}
public DateVar(final String name, final String displayName, final Date value) {
super(name, displayName, value);
}
}
static class BooleanVar extends Var<Boolean> {
public BooleanVar(final String name, final String displayName) {
super(name, displayName);
}
public BooleanVar(final String name, final String displayName, final Boolean value) {
super(name, displayName, value);
}
}
}
जब पैरामीरिज्ड Supplier
समस्या के समाधान की तलाश में , मुझे उपरोक्त उत्तर उपयोगी लगे और सुझाव लागू किए:
private static <T, R> Supplier<String> failedMessageSupplier(Function<String,String> fn, String msgPrefix, String ... customMessages) {
final String msgString = new StringBuilder(msgPrefix).append(" - ").append(String.join("\n", customMessages)).toString();
return () -> fn.apply(msgString);
}
इसे इस तरह लागू किया जाता है:
failedMessageSupplier(String::new, msgPrefix, customMsg);
प्रचुर मात्रा में स्थिर फ़ंक्शन पैरामीटर के साथ अभी तक काफी संतुष्ट नहीं हैं, मैंने आगे और फ़ंक्शन के साथ खोदा। घटना () , मैं निम्नलिखित परिणाम के लिए आया था:
private final static Supplier<String> failedMessageSupplier(final String msgPrefix, final String ... customMessages) {
final String msgString = new StringBuilder(msgPrefix).append(" - ").append(String.join("\n", customMessages)).toString();
return () -> (String)Function.identity().apply(msgString);
};
स्थिर फ़ंक्शन पैरामीटर के बिना अब मंगलाचरण:
failedMessageSupplier(msgPrefix, customMsg)
के बाद से Function.identity()
रिटर्न प्रकार की एक समारोह Object
है, और इसलिए के बाद कॉल करता है apply(msgString)
, के लिए एक डाली String
आवश्यक है - या जो कुछ भी प्रकार, लागू () के साथ खिलाया जा रहा है।
यह विधि कई मापदंडों, गतिशील स्ट्रिंग प्रसंस्करण, स्ट्रिंग स्थिरांक उपसर्ग, प्रत्यय और इतने पर उपयोग करने के लिए अनुमति देता है।
पहचान का उपयोग करना सैद्धांतिक रूप से स्ट्रिंग :: नया पर एक मामूली बढ़त होना चाहिए, जो हमेशा एक नया स्ट्रिंग बनाएगा।
जैसा कि जैकब ज़िमरमैन ने पहले ही बताया, सरल पैरामीरीज़ फॉर्म
Supplier<Foo> makeFooFromString(String str1, String str2) {
return () -> new Foo(str1, str2);
}
हमेशा संभव है। यह एक संदर्भ में समझ में आता है या नहीं, निर्भर करता है।
जैसा कि ऊपर वर्णित है, स्थिर विधि संदर्भ कॉलों को फ़ंक्शन-खपत (स्ट्रीम) विधि द्वारा अपेक्षित से मेल खाने के लिए संबंधित विधि की संख्या और रिटर्न / मापदंडों के प्रकार की आवश्यकता होती है।
यदि आपके पास एक कंस्ट्रक्टर है new Klass(ConstructorObject)
तो आप Function<ConstructorObject, Klass>
इस तरह का उपयोग कर सकते हैं :
interface Interface {
static Klass createKlass(Function<Map<String,Integer>, Klass> func, Map<String, Integer> input) {
return func.apply(input);
}
}
class Klass {
private Integer integer;
Klass(Map<String, Integer> map) {
this.integer = map.get("integer");
}
public static void main(String[] args) {
Map<String, Integer> input = new HashMap<>();
input.put("integer", 1);
Klass klazz = Interface.createKlass(Klass::new, input);
System.out.println(klazz.integer);
}
}