확장 방법을 사용하여 C #에서와 같이 객체 목록에 기능을 구현하려고합니다.
이 같은:
List<DataObject> list;
// ... List initialization.
list.getData(id);
Java로 어떻게합니까?
확장 방법을 사용하여 C #에서와 같이 객체 목록에 기능을 구현하려고합니다.
이 같은:
List<DataObject> list;
// ... List initialization.
list.getData(id);
Java로 어떻게합니까?
답변:
Java는 확장 메소드를 지원하지 않습니다.
대신 일반 정적 메서드를 만들거나 자신의 클래스를 작성할 수 있습니다.
확장 메소드는 정적 메소드 일뿐 아니라 편의 구문 설탕 만이 아니라 실제로는 매우 강력한 도구입니다. 가장 중요한 것은 다른 제네릭의 매개 변수 인스턴스화에 따라 다른 방법을 재정의하는 기능입니다. 이것은 Haskell의 형식 클래스와 유사하며 실제로 C #의 Monads (LINQ)를 지원하기 위해 C #에있는 것처럼 보입니다. LINQ 구문을 삭제하더라도 Java에서 유사한 인터페이스를 구현하는 방법을 아직 모릅니다.
그리고 제네릭 매개 변수의 Java 유형 삭제 의미 때문에 Java로 구현할 수 없다고 생각합니다.
Project Lombok@ExtensionMethod
은 요청한 기능을 달성하는 데 사용할 수 있는 주석 을 제공합니다 .
java.lang.String
기술적으로 C # Extension은 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();
Defender Methods (예 : 기본 메소드)가 Java 8로 만들 수있는 약간의 기회가있는 것 같습니다. 그러나 내가 이해하는 한, 저자 는 interface
임의의 사용자가 아니라 소급하여 확장 할 수 있습니다.
Defender Methods + 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);
}
}
}
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 () 메서드를 호출하여 연결된 지원 개체에 액세스 할 수 있습니다.
확장 인터페이스에는 지정된 지원 객체의 확장을 생성하는 팩토리 메소드가 있어야합니다.
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입니다. 입니다.
다음은 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);
}
}
추신 : 나는 Kotlin 의 큰 팬입니다 . 확장 메소드가 있으며 JVM에서도 실행됩니다.
(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#
확장 메소드 와 유사 합니다.