Ben bir uzantı yöntemi kullanarak C # gibi bir nesne listesinde bir işlevsellik uygulamak için arıyorum.
Bunun gibi bir şey:
List<DataObject> list;
// ... List initialization.
list.getData(id);
Java ile nasıl yapabilirim?
Ben bir uzantı yöntemi kullanarak C # gibi bir nesne listesinde bir işlevsellik uygulamak için arıyorum.
Bunun gibi bir şey:
List<DataObject> list;
// ... List initialization.
list.getData(id);
Java ile nasıl yapabilirim?
Yanıtlar:
Java, uzantı yöntemlerini desteklemez.
Bunun yerine, düzenli bir statik yöntem yapabilir veya kendi sınıfınızı yazabilirsiniz.
Uzatma yöntemleri sadece statik yöntem değildir ve sadece kolaylık sözdizimi şekeri değildir, aslında oldukça güçlü bir araçtır. Ana şey, farklı jenerik parametrelerinin somutlaştırılmasına dayalı olarak farklı yöntemleri geçersiz kılma yeteneğidir. Bu Haskell'in tip sınıflarına benzer ve aslında, C # 'ın Monad'larını (yani LINQ) desteklemek için C #' da gibi görünüyorlar. LINQ sözdizimini bırakarak bile, Java'da benzer arayüzleri uygulamanın hiçbir yolunu bilmiyorum.
Java'nın jenerik parametrelerin tip silme semantiği nedeniyle bunları Java'da uygulamanın mümkün olduğunu düşünmüyorum.
Lombok Projesi@ExtensionMethod
, istediğiniz işlevselliği elde etmek için kullanılabilecek bir ek açıklama sunmaktadır.
java.lang.String
Teknik olarak C # Uzantısı'nın Java'da eşdeğeri yoktur. Ancak, daha temiz bir kod ve sürdürülebilirlik için bu tür işlevleri uygulamak istiyorsanız, Manifold çerçevesini kullanmanız gerekir.
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();
}
}
çeşitli , Java'ya C # stili uzantı yöntemleri ve diğer birçok özellik sağlar. Diğer araçlar aksine, Manifold herhangi bir sınırlama vardır ve gelmez acı jenerik, lambdas, IDE vb Manifold böyle F # tarzı gibi çeşitli diğer özellikler sağlar ile sorunları özel tipleri , typescript tarzı yapısal arayüzleri ve JavaScript tarzı expando türleri .
Ayrıca IntelliJ, Manifold eklentisi aracılığıyla Manifold için kapsamlı destek sağlar .
Manifold geçerli bir açık kaynak projesi olan github .
Başka bir seçenek de google-guava kütüphanesinden ForwardingXXX sınıflarını kullanmaktır .
Java'nın böyle bir özelliği yoktur. Bunun yerine, liste uygulamanızın normal alt sınıfını oluşturabilir veya anonim iç sınıf oluşturabilirsiniz:
List<String> list = new ArrayList<String>() {
public String getData() {
return ""; // add your implementation here.
}
};
Sorun bu yöntemi çağırmaktır. Bunu "yerinde" yapabilirsiniz:
new ArrayList<String>() {
public String getData() {
return ""; // add your implementation here.
}
}.getData();
Defender Yöntemleri (yani varsayılan yöntemleri) Ancak, bildiğim kadarıyla anladığımız şekliyle, sadece izin Java 8. haline getirebilir bazı küçük bir şans var gibi görünüyor yazar bir bir interface
geriye dönük keyfi kullanıcıları değil genişletmek için.
Defender Yöntemleri + Arayüz Enjeksiyonu daha sonra C # tarzı uzatma yöntemlerini tam olarak uygulayabilecektir, ancak AFAICS, Arayüz Enjeksiyonu henüz Java 8 yol haritasında bile değildir.
Bu soruya partiye biraz geç, ancak herkesin faydalı bulması durumunda bir alt sınıf oluşturdum:
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);
}
}
}
Java 8'den beri kullanılabilen varsayılan yöntem uygulamasını kullanarak Java'da C # uzantısı yöntemlerinin uygulanmasını simüle edebiliriz. Bu şekilde, destek nesnesine bir base () yöntemiyle erişmemizi sağlayacak bir arabirim tanımlayarak başlayalım:
public interface Extension<T> {
default T base() {
return null;
}
}
Arabirimlerin durumu olamaz çünkü null döndürüyoruz, ancak bu daha sonra bir proxy aracılığıyla düzeltilmelidir.
Uzantı geliştiricisinin, bu arayüzü, uzantı yöntemlerini içeren yeni bir arayüzle genişletmesi gerekir. Diyelim ki Liste arayüzüne bir forEach tüketici eklemek istiyoruz:
public interface ListExtension<T> extends Extension<List<T>> {
default void foreach(Consumer<T> consumer) {
for (T item : base()) {
consumer.accept(item);
}
}
}
Uzantı arayüzünü genişlettiğimizden, eklediğimiz destek nesnesine erişmek için uzantı yöntemimizin içindeki base () yöntemini çağırabiliriz.
Uzantı arabirimi, belirli bir destek nesnesinin uzantısını oluşturacak bir fabrika yöntemine sahip olmalıdır:
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;
}
}
}
Uzantı arabirimini ve destek nesnesinin türü tarafından uygulanan tüm arabirimi uygulayan bir proxy oluştururuz. Proxy'ye verilen çağırma işleyicisi, destek nesnesini döndürmesi gereken "base" yöntemi dışında, destek nesnesine yapılan tüm çağrıları gönderir, aksi takdirde varsayılan uygulaması null döndürür:
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);
}
}
Ardından, uzantı yöntemini içeren arabirimi destek nesnesine eklemek için Extension.create () yöntemini kullanabiliriz. Sonuç, base () yöntemini çağıran destek nesnesine erişebileceğimiz uzantı arabirimine dökülebilen bir nesnedir. Referansın uzantı arayüzüne dökülmesiyle, artık destek nesnesine erişebilecek uzantı yöntemlerini güvenli bir şekilde çağırabiliriz, böylece şimdi mevcut nesneye yeni yöntemler ekleyebiliriz, ancak tanımlayıcı türüne ekleyemeyiz:
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);
}
}
Yani, bu, Java'da nesneleri, onlara yeni sözleşmeler ekleyerek genişletme yeteneğini simüle etmenin bir yoludur, bu da verilen nesneler üzerinde ek yöntemler çağırmamıza izin verir.
Uzantı arayüzünün kodunu aşağıda bulabilirsiniz:
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;
}
}
}
Birisi dekoratör nesneye yönelik tasarım desenini kullanmak olabilir . Java'nın standart kitaplığında kullanılan bu desene bir örnek DataOutputStream olabilir .
Bir Listenin işlevselliğini artırmak için bazı kodlar:
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: Kotlin'in büyük bir hayranıyım . Uzantı yöntemleri vardır ve ayrıca JVM üzerinde çalışır.
Koleksiyonlar arabirimini uygulayarak ve Java Koleksiyonu için örnek ekleyerek (RE) ile C # benzeri bir uzantı / yardımcı yöntem oluşturabilirsiniz:
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 artık uzantı yöntemlerine benzeyen varsayılan yöntemleri desteklemektedir C#
.