拡張メソッドを使用してC#で行うように、オブジェクトのリストに機能を実装したいと考えています。
このようなもの:
List<DataObject> list;
// ... List initialization.
list.getData(id);
Javaでそれを行うにはどうすればよいですか?
拡張メソッドを使用してC#で行うように、オブジェクトのリストに機能を実装したいと考えています。
このようなもの:
List<DataObject> list;
// ... List initialization.
list.getData(id);
Javaでそれを行うにはどうすればよいですか?
回答:
Javaは拡張メソッドをサポートしていません。
代わりに、通常の静的メソッドを作成するか、独自のクラスを作成できます。
拡張メソッドは単なる静的メソッドではなく、単なる便利な構文糖衣ではなく、実際には非常に強力なツールです。主なものは、さまざまなジェネリックのパラメーターのインスタンス化に基づいてさまざまなメソッドをオーバーライドする機能です。これはHaskellの型クラスに似ており、実際はC#のモナド(つまりLINQ)をサポートするためにC#にあるようです。LINQ構文を削除しても、Javaで同様のインターフェイスを実装する方法はまだわかりません。
そして、Javaのジェネリックパラメーターの型消去のセマンティクスのため、Javaでそれらを実装することは可能ではないと思います。
Project Lombokは、@ExtensionMethod
必要な機能を実現するために使用できる注釈を提供します。
技術的には、C#拡張機能にはJavaに相当するものはありません。ただし、よりクリーンなコードと保守性のためにそのような関数を実装したい場合は、マニホールドフレームワークを使用する必要があります。
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();
}
}
別のオプションは、google-guavaライブラリのForwardingXXXクラスを使用することです。
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);
}
}
}
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;
}
}
}
1つは、デコレータのオブジェクト指向デザインパターンを使用することです。Javaの標準ライブラリで使用されているこのパターンの例は、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私はKotlinの大ファンです。拡張メソッドがあり、JVMでも実行されます。
コレクションインターフェイスを(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#
。これはの拡張メソッドに似ています。