简体   繁体   English

Java 等效于 C# 扩展方法

[英]Java equivalent to C# extension methods

I am looking to implement a functionality in a list of object as I would in C# using an extension method.我希望在对象列表中实现一个功能,就像在 C# 中使用扩展方法一样。

Something like this:像这样:

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

How do I do that in Java?我如何在 Java 中做到这一点?

Java does not support extension methods. Java 不支持扩展方法。

Instead, you can make a regular static method, or write your own class.相反,您可以创建一个常规的静态方法,或者编写您自己的类。

Extension methods are not just static method and not just convenience syntax sugar, in fact they are quite powerful tool.扩展方法不仅仅是静态方法,也不仅仅是方便的语法糖,事实上它们是非常强大的工具。 The main thing there is ability to override different methods based on different generic's parameters instantiation.最主要的是能够根据不同泛型的参数实例化来覆盖不同的方法。 This is similar to Haskell's type classes, and in fact, it looks like they are in C# to support C#'s Monads (ie LINQ).这类似于Haskell 的类型类,实际上,它们看起来像是在C# 中支持C# 的Monads(即LINQ)。 Even dropping LINQ syntax, I still don't know any way to implement similar interfaces in Java.即使放弃 LINQ 语法,我仍然不知道有什么方法可以在 Java 中实现类似的接口。

And I don't think it is possible to implement them in Java, because of Java's type erasure semantics of generics parameters.而且我认为不可能在 Java 中实现它们,因为 Java 的泛型参数类型擦除语义。

Project Lombok provides an annotation @ExtensionMethod that can be used to achieve the functionality you are asking for. Lombok 项目提供了一个注释@ExtensionMethod ,可用于实现您所要求的功能。

Technically C# Extension have no equivalent in Java.从技术上讲,C# 扩展在 Java 中没有等效项。 But if you do want to implement such functions for a cleaner code and maintainability, you have to use Manifold framework.但是如果你确实想实现这样的功能以获得更清晰的代码和可维护性,你必须使用 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();
  }
}

There is no extension methods in Java, but you can have it by manifold as below, Java 中没有扩展方法,但你可以通过如下方式获得它,

You define "echo" method for strings by below sample,您通过以下示例为字符串定义“echo”方法,

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

And after that, you can use this method (echo) for strings anywhere like,在那之后,你可以在任何地方使用这个方法(echo)来处理字符串,比如,

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


You can also, of course, have parameters like,当然,你也可以有像这样的参数,

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

And use like this,像这样使用,

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

You can take a look at this sample also, Manifold-sample你也可以看看这个样本, Manifold-sample

The XTend language — which is a super-set of Java, and compiles to Java source code 1 — supports this. XTend语言——它是 Java 的超集,并编译为 Java 源代码1——支持这一点。

Another option is to use ForwardingXXX classes from google-guava library.另一种选择是使用 google-guava 库中的ForwardingXXX类。

I am looking to implement a functionality in a list of object as I would in C# using an extension method.我希望在对象列表中实现功能,就像在C#中使用扩展方法一样。

Something like this:像这样的东西:

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

How do I do that in Java?我该如何在Java中做到这一点?

It looks like there is some small chance that Defender Methods (ie default methods) might make it into Java 8. However, as far as I understand them, they only allow the author of an interface to retroactively extend it, not arbitrary users.看起来 Defender Methods(即默认方法)可能会进入 Java 8 的可能性很小。但是,据我了解,它们只允许interface作者追溯扩展它,而不是任意用户。

Defender Methods + Interface Injection would then be able to fully implement C#-style extension methods, but AFAICS, Interface Injection isn't even on the Java 8 road-map yet. Defender Methods + Interface Injection 将能够完全实现 C# 风格的扩展方法,但是 AFAICS,Interface Injection 甚至还没有出现在 Java 8 路线图上。

Java does not have such feature. Java 没有这样的功能。 Instead you can either create regular subclass of your list implementation or create anonymous inner class:相反,您可以创建列表实现的常规子类或创建匿名内部类:

List<String> list = new ArrayList<String>() {
   public String getData() {
       return ""; // add your implementation here. 
   }
};

The problem is to call this method.问题是调用这个方法。 You can do it "in place":你可以“就地”做:

new ArrayList<String>() {
   public String getData() {
       return ""; // add your implementation here. 
   }
}.getData();

Bit late to the party on this question, but in case anyone finds it useful I just created a subclass:在这个问题上晚了一点,但如果有人觉得它有用,我只是创建了一个子类:

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

One could be use the decorator object-oriented design pattern .一种可能是使用装饰器面向对象的设计模式 An example of this pattern being used in Java's standard library would be the DataOutputStream . DataOutputStream是 Java 标准库中使用的这种模式的一个示例。

Here's some code for augmenting the functionality of a List:下面是一些用于增强列表功能的代码:

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 I'm a big fan of Kotlin . PS 我是Kotlin的忠实粉丝。 It has extension methods and also runs on the JVM.它有扩展方法,也运行在 JVM 上。

We can simulate the implementation of C# extension methods in Java by using the default method implementation available since Java 8. We start by defining an interface that will allow us to access the support object via a base() method, like so:我们可以通过使用自 Java 8 以来可用的默认方法实现来模拟 Java 中 C# 扩展方法的实现。我们首先定义一个接口,允许我们通过 base() 方法访问支持对象,如下所示:

public interface Extension<T> {

    default T base() {
        return null;
    }
}

We return null since interfaces cannot have state, but this has to be fixed later via a proxy.我们返回 null 因为接口不能有状态,但这必须稍后通过代理修复。

The developer of extensions would have to extend this interface by a new interface containing extension methods.扩展的开发者必须通过一个包含扩展方法的新接口来扩展这个接口。 Let's say we want to add a forEach consumer on List interface:假设我们想在 List 接口上添加一个 forEach 消费者:

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

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

}

Because we extend the Extension interface, we can call base() method inside our extension method to access the support object we attach to.因为我们扩展了 Extension 接口,所以我们可以在扩展方法中调用 base() 方法来访问我们附加的支持对象。

The Extension interface must have a factory method which will create an extension of a given support object: 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;
        }
    }
}

We create a proxy that implements the extension interface and all the interface implemented by the type of the support object.我们创建一个代理,它实现了扩展接口和支持对象类型实现的所有接口。 The invocation handler given to the proxy would dispatch all the calls to the support object, except for the "base" method, which must return the support object, otherwise its default implementation is returning 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);
    }

}

Then, we can use the Extension.create() method to attach the interface containing the extension method to the support object.然后,我们可以使用 Extension.create() 方法将包含扩展方法的接口附加到支持对象。 The result is an object which can be casted to the extension interface by which we can still access the support object calling the base() method.结果是一个可以转换为扩展接口的对象,通过它我们仍然可以访问调用 base() 方法的支持对象。 Having the reference casted to the extension interface, we now can safely call the extension methods that can have access to the support object, so that now we can attach new methods to the existing object, but not to its defining type:将引用转换为扩展接口后,我们现在可以安全地调用可以访问支持对象的扩展方法,这样现在我们可以将新方法附加到现有对象,但不能附加到其定义类型:

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

}

So, this is a way we can simulate the ability to extend objects in Java by adding new contracts to them, which allow us to call additional methods on the given objects.因此,这是一种我们可以通过向对象添加新契约来模拟在 Java 中扩展对象的能力的方法,这允许我们在给定对象上调用其他方法。

Below you may find the code of the Extension interface:您可以在下面找到扩展接口的代码:

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

}

You can create a C# like extension/helper method by (RE) implementing the Collections interface and adding- example for Java Collection:您可以通过 (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 now supports default methods , which are similar to C# 's extension methods. Java 8 现在支持默认方法,类似于C#的扩展方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM