簡體   English   中英

Java 等效於 C# 擴展方法

[英]Java equivalent to C# extension methods

我希望在對象列表中實現一個功能,就像在 C# 中使用擴展方法一樣。

像這樣:

List<DataObject> list;
// ... List initialization.
list.getData(id);

我如何在 Java 中做到這一點?

Java 不支持擴展方法。

相反,您可以創建一個常規的靜態方法,或者編寫您自己的類。

擴展方法不僅僅是靜態方法,也不僅僅是方便的語法糖,事實上它們是非常強大的工具。 最主要的是能夠根據不同泛型的參數實例化來覆蓋不同的方法。 這類似於Haskell 的類型類,實際上,它們看起來像是在C# 中支持C# 的Monads(即LINQ)。 即使放棄 LINQ 語法,我仍然不知道有什么方法可以在 Java 中實現類似的接口。

而且我認為不可能在 Java 中實現它們,因為 Java 的泛型參數類型擦除語義。

Lombok 項目提供了一個注釋@ExtensionMethod ,可用於實現您所要求的功能。

從技術上講,C# 擴展在 Java 中沒有等效項。 但是如果你確實想實現這樣的功能以獲得更清晰的代碼和可維護性,你必須使用 Manifold 框架。

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();
  }
}

Java 中沒有擴展方法,但你可以通過如下方式獲得它,

您通過以下示例為字符串定義“echo”方法,

@Extension
public class MyStringExtension {
    public static void echo(@This String thiz) {
        System.out.println(thiz);
    }
}

在那之后,你可以在任何地方使用這個方法(echo)來處理字符串,比如,

"Hello World".echo();   //prints "Hello World"
"Welcome".echo();       //prints "Welcome"
String name = "Jonn";
name.echo();            //prints "John"


當然,你也可以有像這樣的參數,

@Extension
public class MyStringExtension {
    public static void echo(@This String thiz, String suffix) {
        System.out.println(thiz + " " + suffix);
    }
}

像這樣使用,

"Hello World".echo("programming");   //prints "Hello World programming"
"Welcome".echo("2021");              //prints "Welcome 2021"
String name = "John";
name.echo("Conor");                  //prints "John Conor"

你也可以看看這個樣本, Manifold-sample

XTend語言——它是 Java 的超集,並編譯為 Java 源代碼1——支持這一點。

另一種選擇是使用 google-guava 庫中的ForwardingXXX類。

我希望在對象列表中實現功能,就像在C#中使用擴展方法一樣。

像這樣的東西:

List<DataObject> list;
// ... List initialization.
list.getData(id);

我該如何在Java中做到這一點?

看起來 Defender Methods(即默認方法)可能會進入 Java 8 的可能性很小。但是,據我了解,它們只允許interface作者追溯擴展它,而不是任意用戶。

Defender Methods + Interface Injection 將能夠完全實現 C# 風格的擴展方法,但是 AFAICS,Interface Injection 甚至還沒有出現在 Java 8 路線圖上。

Java 沒有這樣的功能。 相反,您可以創建列表實現的常規子類或創建匿名內部類:

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();

在這個問題上晚了一點,但如果有人覺得它有用,我只是創建了一個子類:

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);
        }
    }
}

一種可能是使用裝飾器面向對象的設計模式 DataOutputStream是 Java 標准庫中使用的這種模式的一個示例。

下面是一些用於增強列表功能的代碼:

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的忠實粉絲。 它有擴展方法,也運行在 JVM 上。

我們可以通過使用自 Java 8 以來可用的默認方法實現來模擬 Java 中 C# 擴展方法的實現。我們首先定義一個接口,允許我們通過 base() 方法訪問支持對象,如下所示:

public interface Extension<T> {

    default T base() {
        return null;
    }
}

我們返回 null 因為接口不能有狀態,但這必須稍后通過代理修復。

擴展的開發者必須通過一個包含擴展方法的新接口來擴展這個接口。 假設我們想在 List 接口上添加一個 forEach 消費者:

public interface ListExtension<T> extends Extension<List<T>> {

    default void foreach(Consumer<T> consumer) {
        for (T item : base()) {
            consumer.accept(item);
        }
    }

}

因為我們擴展了 Extension 接口,所以我們可以在擴展方法中調用 base() 方法來訪問我們附加的支持對象。

Extension 接口必須有一個工廠方法,它將創建給定支持對象的擴展:

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;
        }
    }
}

我們創建一個代理,它實現了擴展接口和支持對象類型實現的所有接口。 提供給代理的調用處理程序會將所有調用分派給支持對象,“base”方法除外,它必須返回支持對象,否則其默認實現返回 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() 方法將包含擴展方法的接口附加到支持對象。 結果是一個可以轉換為擴展接口的對象,通過它我們仍然可以訪問調用 base() 方法的支持對象。 將引用轉換為擴展接口后,我們現在可以安全地調用可以訪問支持對象的擴展方法,這樣現在我們可以將新方法附加到現有對象,但不能附加到其定義類型:

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);
    }

}

因此,這是一種我們可以通過向對象添加新契約來模擬在 Java 中擴展對象的能力的方法,這允許我們在給定對象上調用其他方法。

您可以在下面找到擴展接口的代碼:

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;
        }
    }

}

您可以通過 (RE) 實現 Collections 接口並為 Java Collection 添加示例來創建類似 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#的擴展方法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM