C# 확장 메서드에 상당하는 Java
확장 방식을 사용하여 C#에서와 같이 오브젝트 목록에 기능을 구현하려고 합니다.
다음과 같은 경우:
List<DataObject> list;
// ... List initialization.
list.getData(id);
자바에서는 어떻게 해야 하나요?
Java는 확장 메서드를 지원하지 않습니다.
대신 일반 정적 메서드를 만들거나 자체 클래스를 작성할 수 있습니다.
확장 방법은 정적 방법뿐만 아니라 편리한 구문설탕도 아닙니다. 사실 매우 강력한 도구입니다.여기서 중요한 것은 다른 범용 파라미터 인스턴스화에 따라 다른 메서드를 덮어쓸 수 있다는 것입니다.이것은 Haskell의 유형 클래스와 비슷하며, 실제로는 C#의 Monads(LINQ)를 지원하기 위해 C#에 있는 것처럼 보입니다.LINQ 구문을 삭제해도 Java에서 유사한 인터페이스를 구현하는 방법은 아직 모릅니다.
자바에서는 범용 파라미터의 타입 삭제 시멘틱스 때문에 구현이 불가능하다고 생각합니다.
Project Lombok은 원하는 기능을 구현하기 위해 사용할 수 있는 주석을 제공합니다.
기술적으로 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();
}
}
매니폴드는 Java에 C# 스타일의 확장 메서드 및 기타 몇 가지 기능을 제공합니다.다른 도구와 달리, 매니폴드는 제한이 없으며 제네릭, 람다, IDE 등의 문제를 겪지 않습니다.매니폴드는 F# 스타일의 커스텀타입, TypeScript 스타일의 구조 인터페이스, Javascript 스타일의 expando 타입과 같은 몇 가지 다른 기능을 제공합니다.
또한 IntelliJ는 매니폴드 플러그인을 통해 매니폴드를 포괄적으로 지원합니다.
매니폴드는 github에서 사용할 수 있는 오픈 소스 프로젝트입니다.
Java의 슈퍼셋으로 Java1 소스 코드로 컴파일된 XTend 언어가 이를 지원합니다.
다른 옵션은 Google-guava 라이브러리의 Forwarding XXX 클래스를 사용하는 것입니다.
자바에서는 확장방식은 없지만 아래와 같이 다지관별로 사용할 수 있습니다.
아래 샘플에서 문자열에 대한 "에코" 메서드를 정의합니다.
@Extension
public class MyStringExtension {
public static void echo(@This String thiz) {
System.out.println(thiz);
}
}
그 후, 이 메서드(에코)를 사용하면, 다음과 같은 문자열에 사용할 수 있습니다.
"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"
이 표본도 볼 수 있습니다. 다지관 표본
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 메서드(즉, 기본 메서드)가 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을 반환하지만 나중에 프록시를 통해 수정해야 합니다.
확장 개발자는 확장 메서드를 포함하는 새로운 인터페이스를 사용하여 이 인터페이스를 확장해야 합니다.예를 들어 목록인터페이스에 각 사용자용 을 추가합니다.
public interface ListExtension<T> extends Extension<List<T>> {
default void foreach(Consumer<T> consumer) {
for (T item : base()) {
consumer.accept(item);
}
}
}
확장 인터페이스를 확장하므로 확장 메서드 내의 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;
}
}
}
확장 인터페이스와 지원 오브젝트 유형에 따라 구현된 모든 인터페이스를 구현하는 프록시를 만듭니다.프록시에 지정된 호출핸들러는 지원 객체를 반환해야 하는 "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;
}
}
}
하나는 데코레이터 객체 지향 디자인 패턴을 사용할 수 있습니다.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);
}
}
추신: 저는 코틀린의 광팬입니다.확장 메서드가 있으며 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#
의 확장 메서드입니다.
언급URL : https://stackoverflow.com/questions/4359979/java-equivalent-to-c-sharp-extension-methods
'source' 카테고리의 다른 글
vuejs 요리책의 편집 가능한 Svg 아이콘 시스템에서 아이콘 색상을 변경하는 방법 (0) | 2022.08.21 |
---|---|
vuex-module-decorator에서 MutationAction을 사용하는 방법 (0) | 2022.08.21 |
Vue js는 v-on 또는 @click을 사용하지 않고 클릭 이벤트를 추가합니다. (0) | 2022.08.21 |
포인터가 C를 가리키고 있는 함수를 판별하고 있습니까? (0) | 2022.08.21 |
C/C++ 문자 크기('a') (0) | 2022.08.21 |