मैं ऑब्जेक्ट की एक सूची में एक कार्यक्षमता को लागू करने के लिए देख रहा हूं जैसा कि मैं C # में एक एक्सटेंशन विधि का उपयोग कर रहा हूं।
कुछ इस तरह:
List<DataObject> list;
// ... List initialization.
list.getData(id);
मैं जावा में कैसे करूँ?
मैं ऑब्जेक्ट की एक सूची में एक कार्यक्षमता को लागू करने के लिए देख रहा हूं जैसा कि मैं C # में एक एक्सटेंशन विधि का उपयोग कर रहा हूं।
कुछ इस तरह:
List<DataObject> list;
// ... List initialization.
list.getData(id);
मैं जावा में कैसे करूँ?
जवाबों:
जावा एक्सटेंशन के तरीकों का समर्थन नहीं करता है।
इसके बजाय, आप एक नियमित स्थैतिक विधि बना सकते हैं, या अपनी कक्षा लिख सकते हैं।
एक्सटेंशन विधियां केवल स्थैतिक विधि नहीं हैं और न केवल सुविधा सिंटैक्स चीनी, वास्तव में वे काफी शक्तिशाली उपकरण हैं। मुख्य बात यह है कि विभिन्न जेनेरिक के मापदंडों के आधार पर विभिन्न तरीकों को ओवरराइड करने की क्षमता है। यह हास्केल के प्रकार वर्गों के समान है, और वास्तव में, ऐसा लगता है कि वे C # के मोनड्स (यानी LINQ) का समर्थन करने के लिए C # में हैं। यहां तक कि LINQ सिंटैक्स को छोड़ने पर, मुझे अभी भी जावा में समान इंटरफेस को लागू करने का कोई तरीका नहीं पता है।
और मुझे नहीं लगता कि जावा के जेनेरिक मापदंडों के प्रकार के मिटाए जाने वाले शब्दार्थ के कारण उन्हें जावा में लागू करना संभव है।
प्रोजेक्ट लोम्बोक एक एनोटेशन प्रदान करता है@ExtensionMethod
है जिसका उपयोग उस कार्यक्षमता को प्राप्त करने के लिए किया जा सकता है जिसे आप पूछ रहे हैं।
java.lang.String
। प्रदर्शन: http://manifold.systems/images/ExtensionMethod.mp4
तकनीकी रूप से C # एक्सटेंशन का जावा में कोई समकक्ष नहीं है। लेकिन अगर आप क्लीनर कोड और रखरखाव के लिए ऐसे कार्यों को लागू करना चाहते हैं, तो आपको मैनिफोल्ड ढांचे का उपयोग करना होगा।
package extensions.java.lang.String;
import manifold.ext.api.*;
@Extension
public class MyStringExtension {
public static void print(@This String thiz) {
System.out.println(thiz);
}
@Extension
public static String lineSeparator() {
return System.lineSeparator();
}
}
मैनिफोल्ड जावा को C # -स्टाइल विस्तार विधियों और कई अन्य विशेषताओं के साथ प्रदान करता है। अन्य उपकरणों के विपरीत, मैनिफोल्ड की कोई सीमा नहीं है और जेनेरिक, लैम्ब्डा, आईडीई आदि के साथ समस्याओं से ग्रस्त नहीं है । मैनिफोल्ड कई अन्य विशेषताएं प्रदान करता है जैसे कि एफ # -स्टाइल कस्टम प्रकार , टाइपस्क्रिप्ट-स्टाइल संरचनात्मक इंटरफेस , और जावास्क्रिप्ट-शैली विस्तार प्रकार ।
इसके अतिरिक्त, IntelliJ मैनिफोल्ड प्लगइन के माध्यम से कई गुना व्यापक समर्थन प्रदान करता है ।
मैनिफोल्ड एक ओपन सोर्स प्रोजेक्ट है जो गीथब पर उपलब्ध है ।
एक अन्य विकल्प है कि गूगल-अमरूद लाइब्रेरी से फॉरवर्डिंगएक्सएक्सएक्स क्लास का उपयोग किया जाए ।
जावा में ऐसी सुविधा नहीं है। इसके बजाय आप अपनी सूची कार्यान्वयन के नियमित उपवर्ग बना सकते हैं या अनाम आंतरिक वर्ग बना सकते हैं:
List<String> list = new ArrayList<String>() {
public String getData() {
return ""; // add your implementation here.
}
};
समस्या इस पद्धति को कॉल करने की है। आप इसे "जगह में" कर सकते हैं:
new ArrayList<String>() {
public String getData() {
return ""; // add your implementation here.
}
}.getData();
ऐसा लगता है कि कुछ छोटे मौके हैं जो डिफेंडर मेथड्स (यानी डिफॉल्ट मेथड्स) इसे जावा 8 में बना सकते हैं। हालांकि, जहां तक मैं उन्हें समझता हूं, वे केवल एक के लेखकinterface
को पीछे हटने की इजाजत देते हैं , न कि यूजर्स को।
डिफेंडर के तरीके + इंटरफ़ेस इंजेक्शन फिर सी # -स्टाइल एक्सटेंशन विधियों को पूरी तरह से लागू करने में सक्षम होगा, लेकिन AFAICS, इंटरफ़ेस इंजेक्शन अभी भी जावा 8 रोड-मैप पर नहीं है।
इस प्रश्न पर पार्टी के लिए देर से, लेकिन किसी को भी यह उपयोगी लगता है तो मैंने सिर्फ एक उपवर्ग बनाया:
public class ArrayList2<T> extends ArrayList<T>
{
private static final long serialVersionUID = 1L;
public T getLast()
{
if (this.isEmpty())
{
return null;
}
else
{
return this.get(this.size() - 1);
}
}
}
हम जावा 8 में उपलब्ध डिफ़ॉल्ट विधि कार्यान्वयन का उपयोग करके जावा में C # एक्सटेंशन विधियों के कार्यान्वयन का अनुकरण कर सकते हैं। हम एक इंटरफ़ेस को परिभाषित करके शुरू करते हैं जो हमें आधार () विधि के माध्यम से समर्थन ऑब्जेक्ट तक पहुंचने की अनुमति देगा, जैसे:
public interface Extension<T> {
default T base() {
return null;
}
}
हम शून्य वापस आते हैं क्योंकि इंटरफेस में स्थिति नहीं हो सकती है, लेकिन इसे बाद में प्रॉक्सी के माध्यम से तय किया जाना चाहिए।
एक्सटेंशन के डेवलपर को इस इंटरफ़ेस को एक्सटेंशन विधियों से युक्त एक नए इंटरफ़ेस द्वारा विस्तारित करना होगा। मान लें कि हम सूची इंटरफ़ेस पर एक पूर्व उपभोक्ता जोड़ना चाहते हैं:
public interface ListExtension<T> extends Extension<List<T>> {
default void foreach(Consumer<T> consumer) {
for (T item : base()) {
consumer.accept(item);
}
}
}
चूँकि हम एक्सटेंशन इंटरफ़ेस का विस्तार करते हैं, इसलिए हम अपने द्वारा अटैच किए गए समर्थन ऑब्जेक्ट तक पहुंचने के लिए बेस () विधि को अपनी एक्सटेंशन विधि के अंदर कॉल कर सकते हैं।
एक्सटेंशन इंटरफ़ेस में फ़ैक्टरी विधि होनी चाहिए जो किसी दिए गए समर्थन ऑब्जेक्ट का एक्सटेंशन बनाएगी:
public interface Extension<T> {
...
static <E extends Extension<T>, T> E create(Class<E> type, T instance) {
if (type.isInterface()) {
ExtensionHandler<T> handler = new ExtensionHandler<T>(instance);
List<Class<?>> interfaces = new ArrayList<Class<?>>();
interfaces.add(type);
Class<?> baseType = type.getSuperclass();
while (baseType != null && baseType.isInterface()) {
interfaces.add(baseType);
baseType = baseType.getSuperclass();
}
Object proxy = Proxy.newProxyInstance(
Extension.class.getClassLoader(),
interfaces.toArray(new Class<?>[interfaces.size()]),
handler);
return type.cast(proxy);
} else {
return null;
}
}
}
हम एक प्रॉक्सी बनाते हैं जो एक्सटेंशन इंटरफ़ेस और समर्थन ऑब्जेक्ट के प्रकार द्वारा लागू किए गए सभी इंटरफ़ेस को लागू करता है। प्रॉक्सी को दिया गया इनवोकेशन हैंडलर "बेस" विधि को छोड़कर सभी कॉल को सपोर्ट ऑब्जेक्ट में भेज देगा, जिसे सपोर्ट ऑब्जेक्ट को वापस करना होगा, अन्यथा इसका डिफॉल्ट इम्प्लीमेंट शून्य हो रहा है:
public class ExtensionHandler<T> implements InvocationHandler {
private T instance;
private ExtensionHandler(T instance) {
this.instance = instance;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if ("base".equals(method.getName())
&& method.getParameterCount() == 0) {
return instance;
} else {
Class<?> type = method.getDeclaringClass();
MethodHandles.Lookup lookup = MethodHandles.lookup()
.in(type);
Field allowedModesField = lookup.getClass().getDeclaredField("allowedModes");
makeFieldModifiable(allowedModesField);
allowedModesField.set(lookup, -1);
return lookup
.unreflectSpecial(method, type)
.bindTo(proxy)
.invokeWithArguments(args);
}
}
private static void makeFieldModifiable(Field field) throws Exception {
field.setAccessible(true);
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField
.setInt(field, field.getModifiers() & ~Modifier.FINAL);
}
}
फिर, हम एक्सटेंशन ऑब्जेक्ट को सपोर्ट ऑब्जेक्ट में इंटरफ़ेस को संलग्न करने के लिए Extension.create () विधि का उपयोग कर सकते हैं। परिणाम एक ऑब्जेक्ट है जिसे एक्सटेंशन इंटरफ़ेस में डाला जा सकता है जिसके द्वारा हम आधार () विधि को कॉल करके अभी भी समर्थन ऑब्जेक्ट तक पहुंच सकते हैं। एक्सटेंशन इंटरफ़ेस में डाले गए संदर्भ के बाद, अब हम उन एक्सटेंशन विधियों को सुरक्षित रूप से कॉल कर सकते हैं, जिनकी सहायता ऑब्जेक्ट तक पहुँच हो सकती है, ताकि अब हम मौजूदा ऑब्जेक्ट में नए तरीके संलग्न कर सकें, लेकिन इसके परिभाषित प्रकार के लिए नहीं:
public class Program {
public static void main(String[] args) {
List<String> list = Arrays.asList("a", "b", "c");
ListExtension<String> listExtension = Extension.create(ListExtension.class, list);
listExtension.foreach(System.out::println);
}
}
तो, यह एक ऐसा तरीका है जिससे हम जावा में वस्तुओं का विस्तार करने की क्षमता को नए अनुबंधों के साथ जोड़ सकते हैं, जो हमें दी गई वस्तुओं पर अतिरिक्त तरीकों को कॉल करने की अनुमति देता है।
नीचे आपको एक्सटेंशन इंटरफ़ेस का कोड मिल सकता है:
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
public interface Extension<T> {
public class ExtensionHandler<T> implements InvocationHandler {
private T instance;
private ExtensionHandler(T instance) {
this.instance = instance;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if ("base".equals(method.getName())
&& method.getParameterCount() == 0) {
return instance;
} else {
Class<?> type = method.getDeclaringClass();
MethodHandles.Lookup lookup = MethodHandles.lookup()
.in(type);
Field allowedModesField = lookup.getClass().getDeclaredField("allowedModes");
makeFieldModifiable(allowedModesField);
allowedModesField.set(lookup, -1);
return lookup
.unreflectSpecial(method, type)
.bindTo(proxy)
.invokeWithArguments(args);
}
}
private static void makeFieldModifiable(Field field) throws Exception {
field.setAccessible(true);
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
}
}
default T base() {
return null;
}
static <E extends Extension<T>, T> E create(Class<E> type, T instance) {
if (type.isInterface()) {
ExtensionHandler<T> handler = new ExtensionHandler<T>(instance);
List<Class<?>> interfaces = new ArrayList<Class<?>>();
interfaces.add(type);
Class<?> baseType = type.getSuperclass();
while (baseType != null && baseType.isInterface()) {
interfaces.add(baseType);
baseType = baseType.getSuperclass();
}
Object proxy = Proxy.newProxyInstance(
Extension.class.getClassLoader(),
interfaces.toArray(new Class<?>[interfaces.size()]),
handler);
return type.cast(proxy);
} else {
return null;
}
}
}
एक डेकोरेटर ऑब्जेक्ट-ओरिएंटेड डिज़ाइन पैटर्न का उपयोग कर सकता है । जावा के मानक पुस्तकालय में उपयोग किए जा रहे इस पैटर्न का एक उदाहरण DataOutputStream होगा ।
सूची की कार्यक्षमता बढ़ाने के लिए यहां कुछ कोड दिए गए हैं:
public class ListDecorator<E> implements List<E>
{
public final List<E> wrapee;
public ListDecorator(List<E> wrapee)
{
this.wrapee = wrapee;
}
// implementation of all the list's methods here...
public <R> ListDecorator<R> map(Transform<E,R> transformer)
{
ArrayList<R> result = new ArrayList<R>(size());
for (E element : this)
{
R transformed = transformer.transform(element);
result.add(transformed);
}
return new ListDecorator<R>(result);
}
}
PS मैं कोटलिन का बहुत बड़ा प्रशंसक हूं । इसमें विस्तार के तरीके हैं और यह जेवीएम पर भी चलता है।
आप (RE) कलेक्शन इंटरफेस को लागू करके और Java कलेक्शन के लिए उदाहरण जोड़कर C # जैसे एक्सटेंशन / हेल्पर विधि बना सकते हैं:
public class RockCollection<T extends Comparable<T>> implements Collection<T> {
private Collection<T> _list = new ArrayList<T>();
//###########Custom extension methods###########
public T doSomething() {
//do some stuff
return _list
}
//proper examples
public T find(Predicate<T> predicate) {
return _list.stream()
.filter(predicate)
.findFirst()
.get();
}
public List<T> findAll(Predicate<T> predicate) {
return _list.stream()
.filter(predicate)
.collect(Collectors.<T>toList());
}
public String join(String joiner) {
StringBuilder aggregate = new StringBuilder("");
_list.forEach( item ->
aggregate.append(item.toString() + joiner)
);
return aggregate.toString().substring(0, aggregate.length() - 1);
}
public List<T> reverse() {
List<T> listToReverse = (List<T>)_list;
Collections.reverse(listToReverse);
return listToReverse;
}
public List<T> sort(Comparator<T> sortComparer) {
List<T> listToReverse = (List<T>)_list;
Collections.sort(listToReverse, sortComparer);
return listToReverse;
}
public int sum() {
List<T> list = (List<T>)_list;
int total = 0;
for (T aList : list) {
total += Integer.parseInt(aList.toString());
}
return total;
}
public List<T> minus(RockCollection<T> listToMinus) {
List<T> list = (List<T>)_list;
int total = 0;
listToMinus.forEach(list::remove);
return list;
}
public Double average() {
List<T> list = (List<T>)_list;
Double total = 0.0;
for (T aList : list) {
total += Double.parseDouble(aList.toString());
}
return total / list.size();
}
public T first() {
return _list.stream().findFirst().get();
//.collect(Collectors.<T>toList());
}
public T last() {
List<T> list = (List<T>)_list;
return list.get(_list.size() - 1);
}
//##############################################
//Re-implement existing methods
@Override
public int size() {
return _list.size();
}
@Override
public boolean isEmpty() {
return _list == null || _list.size() == 0;
}
Java
8 अब डिफ़ॉल्ट विधियों का समर्थन करता है , जो कि C#
विस्तार विधियों के समान हैं ।