[英]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
另一種選擇是使用 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.