Я ищу для реализации функциональности в списке объектов, как я бы в C #, используя метод расширения.
Что-то вроде этого:
List<DataObject> list;
// ... List initialization.
list.getData(id);
Как мне это сделать на Java?
Я ищу для реализации функциональности в списке объектов, как я бы в C #, используя метод расширения.
Что-то вроде этого:
List<DataObject> list;
// ... List initialization.
list.getData(id);
Как мне это сделать на Java?
Ответы:
Java не поддерживает методы расширения.
Вместо этого вы можете создать обычный статический метод или написать свой собственный класс.
Методы расширения - это не просто статический метод и не просто удобство синтаксиса, на самом деле это довольно мощный инструмент. Главное, что есть возможность переопределять разные методы, основанные на создании экземпляров различных родовых параметров. Это похоже на классы типов в Haskell, и, на самом деле, похоже, что они находятся в C # для поддержки монад C # (т.е. LINQ). Даже отбрасывая синтаксис LINQ, я до сих пор не знаю способа реализации подобных интерфейсов в Java.
И я не думаю, что возможно реализовать их в Java, из-за семантики стирания типов Java обобщенных параметров.
Проект Ломбок предоставляет аннотацию, @ExtensionMethod
которую можно использовать для достижения требуемой функциональности.
java.lang.String
. Демонстрация: http://manifold.systems/images/ExtensionMethod.mp4
Технически расширение 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();
}
}
Язык XTend - который является супернабором Java и компилируется в исходный код Java 1 - поддерживает это.
Коллектор предоставляет Java методы расширения в стиле C # и ряд других функций. В отличие от других инструментов, Manifold не имеет ограничений и не страдает от проблем с обобщениями, лямбдами, IDE и т. Д. Manifold предоставляет несколько других функций, таких как настраиваемые типы в стиле F # , структурные интерфейсы в стиле TypeScript и расширяемые типы в стиле Javascript .
Кроме того, IntelliJ обеспечивает всестороннюю поддержку Manifold через плагин Manifold .
Manifold - это проект с открытым исходным кодом, доступный на github .
Другой вариант - использовать классы ForwardingXXX из библиотеки google-guava.
У 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();
Похоже , есть некоторые небольшие шансы , что Защитник метода (т.е. метода по умолчанию) может сделать это в Java 8. Однако, насколько я понимаю их, они лишь позволяют автор вinterface
заднем числе продлить его, а не произвольные пользователь.
Методы Defender + Interface Injection смогут полностью реализовать методы расширения в стиле C #, но AFAICS, Interface Injection даже не входит в план развития Java 8.
Немного опоздал на вечеринку по этому вопросу, но на тот случай, если кто-то сочтет это полезным, я просто создал подкласс
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);
}
}
}
Мы можем смоделировать реализацию методов расширения C # в Java, используя реализацию метода по умолчанию, доступную начиная с Java 8. Мы начнем с определения интерфейса, который позволит нам получить доступ к объекту поддержки через метод base (), например, так:
public interface Extension<T> {
default T base() {
return null;
}
}
Мы возвращаем null, так как интерфейсы не могут иметь состояния, но это должно быть исправлено позже через прокси.
Разработчик расширений должен был бы расширить этот интерфейс новым интерфейсом, содержащим методы расширения. Допустим, мы хотим добавить потребителя forEach в интерфейс List:
public interface ListExtension<T> extends Extension<List<T>> {
default void foreach(Consumer<T> consumer) {
for (T item : base()) {
consumer.accept(item);
}
}
}
Поскольку мы расширяем интерфейс Extension, мы можем вызвать метод base () внутри нашего метода extension, чтобы получить доступ к объекту поддержки, к которому мы присоединяемся.
Интерфейс 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;
}
}
}
Мы создаем прокси, который реализует интерфейс расширения и весь интерфейс, реализованный по типу объекта поддержки. Обработчик вызова, данный прокси, будет отправлять все вызовы к объекту поддержки, за исключением «базового» метода, который должен возвращать объект поддержки, в противном случае его реализация по умолчанию возвращает нуль:
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;
}
}
}
Можно было бы использовать декоратор объектно-ориентированного шаблона проектирования . Примером этого шаблона, используемого в стандартной библиотеке 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.
Вы можете создать C # -подобный метод расширения / помощника путем (RE) реализации интерфейса Collections и добавления-примера для Java Collection:
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#
методы расширения.