Eu estou olhando para implementar uma funcionalidade em uma lista de objetos como faria em C # usando um método de extensão.
Algo assim:
List<DataObject> list;
// ... List initialization.
list.getData(id);
Como faço isso em Java?
Eu estou olhando para implementar uma funcionalidade em uma lista de objetos como faria em C # usando um método de extensão.
Algo assim:
List<DataObject> list;
// ... List initialization.
list.getData(id);
Como faço isso em Java?
Respostas:
Java não suporta métodos de extensão.
Em vez disso, você pode criar um método estático regular ou escrever sua própria classe.
Os métodos de extensão não são apenas métodos estáticos e não apenas açúcar de sintaxe de conveniência, na verdade eles são uma ferramenta bastante poderosa. O principal é a capacidade de substituir métodos diferentes com base na instanciação de parâmetros de genéricos diferentes. Isso é semelhante às classes de tipo de Haskell e, de fato, parece que elas estão em C # para suportar as mônadas de C # (ou seja, LINQ). Mesmo eliminando a sintaxe do LINQ, ainda não conheço nenhuma maneira de implementar interfaces semelhantes em Java.
E não creio que seja possível implementá-los em Java, devido à semântica de apagamento de tipo de Java dos parâmetros genéricos.
O Projeto Lombok fornece uma anotação @ExtensionMethod
que pode ser usada para obter a funcionalidade que você está solicitando.
java.lang.String
,. Demonstração: http://manifold.systems/images/ExtensionMethod.mp4
Tecnicamente, a extensão C # não tem equivalente em Java. Mas se você deseja implementar essas funções para obter um código e uma manutenção mais limpos, use a estrutura do 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();
}
}
A linguagem XTend - que é um superconjunto de Java e compila no código-fonte 1 do Java - suporta isso.
O Manifold fornece ao Java métodos de extensão no estilo C # e vários outros recursos. Diferentemente de outras ferramentas, o Manifold não tem limitações e não sofre problemas com genéricos, lambdas, IDE etc. O Manifold fornece vários outros recursos, como tipos personalizados no estilo F # , interfaces estruturais no estilo TypeScript e tipos expando no estilo Javascript .
Além disso, o IntelliJ fornece suporte abrangente ao Manifold por meio do plug-in do Manifold .
Manifold é um projeto de código aberto disponível no github .
Outra opção é usar as classes ForwardingXXX da biblioteca do google-goiaba.
Java não possui esse recurso. Em vez disso, você pode criar subclasses regulares da implementação da sua lista ou criar uma classe interna anônima:
List<String> list = new ArrayList<String>() {
public String getData() {
return ""; // add your implementation here.
}
};
O problema é chamar esse método. Você pode fazer isso "no lugar":
new ArrayList<String>() {
public String getData() {
return ""; // add your implementation here.
}
}.getData();
Parece que há uma pequena chance de que os Métodos Defender (ou seja, métodos padrão) possam entrar no Java 8. No entanto, até onde eu os entendo, eles permitem apenas que o autor de um interface
estenda-o retroativamente, e não usuários arbitrários.
O Defender Methods + Interface Injection seria capaz de implementar completamente os métodos de extensão no estilo C #, mas o AFAICS, Interface Injection ainda não está no roteiro do Java 8.
Um pouco atrasado para a parte sobre esta questão, mas, caso alguém ache útil, acabei de criar uma subclasse:
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);
}
}
}
Podemos simular a implementação dos métodos de extensão C # em Java usando a implementação padrão disponível desde o Java 8. Começamos definindo uma interface que nos permitirá acessar o objeto de suporte por meio de um método base (), como:
public interface Extension<T> {
default T base() {
return null;
}
}
Retornamos nulos, pois as interfaces não podem ter estado, mas isso deve ser corrigido posteriormente por meio de um proxy.
O desenvolvedor de extensões teria que estender essa interface por uma nova interface contendo métodos de extensão. Digamos que queremos adicionar um consumidor forEach na interface List:
public interface ListExtension<T> extends Extension<List<T>> {
default void foreach(Consumer<T> consumer) {
for (T item : base()) {
consumer.accept(item);
}
}
}
Como estendemos a interface Extension, podemos chamar o método base () dentro do nosso método de extensão para acessar o objeto de suporte ao qual anexamos.
A interface Extension deve ter um método de fábrica que criará uma extensão de um determinado objeto de suporte:
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;
}
}
}
Criamos um proxy que implementa a interface de extensão e toda a interface implementada pelo tipo do objeto de suporte. O manipulador de chamada fornecido ao proxy enviaria todas as chamadas para o objeto de suporte, exceto o método "base", que deve retornar o objeto de suporte, caso contrário, sua implementação padrão retornará nulo:
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);
}
}
Em seguida, podemos usar o método Extension.create () para anexar a interface que contém o método de extensão ao objeto de suporte. O resultado é um objeto que pode ser convertido para a interface de extensão pela qual ainda podemos acessar o objeto de suporte que chama o método base (). Tendo a referência convertida para a interface de extensão, agora podemos chamar com segurança os métodos de extensão que podem ter acesso ao objeto de suporte, para que agora possamos anexar novos métodos ao objeto existente, mas não ao seu tipo de definição:
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);
}
}
Portanto, é uma maneira de simular a capacidade de estender objetos em Java adicionando novos contratos a eles, o que nos permite chamar métodos adicionais para os objetos fornecidos.
Abaixo, você pode encontrar o código da interface Extension:
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;
}
}
}
Pode-se usar o padrão de design orientado a objetos do decorador . Um exemplo desse padrão usado na biblioteca padrão de Java seria o DataOutputStream .
Aqui está um código para aumentar a funcionalidade de uma lista:
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 Eu sou um grande fã de Kotlin . Possui métodos de extensão e também é executado na JVM.
Você pode criar um método de extensão / auxiliar do tipo C # implementando a interface Collections e adicionando um exemplo para o 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 agora suporta métodos padrão , que são semelhantes aos C#
métodos de extensão.