자바에서 가장 가까운 함수 포인터는 무엇입니까?
약 10줄의 코드가 있는 방법이 있어요.코드 한 줄을 바꾸는 작은 계산만 제외하고 동일한 작업을 수행하는 방법을 더 만들고 싶습니다.이것은 함수 포인터를 전달하여 한 줄을 대체하기 위한 완벽한 애플리케이션이지만, Java에는 함수 포인터가 없습니다.어떻게 하면 좋을까요?
익명 내부 클래스
를 들어 '를 '함수'로 하고 요.String
" "를하는 paramint
.
먼저 기존 인터페이스를 재사용할 수 없는 경우 해당 기능을 가진 인터페이스를 유일한 멤버로 정의해야 합니다.
interface StringFunction {
int func(String param);
}
.StringFunction
다음과 같이 합니다.
public void takingMethod(StringFunction sf) {
int i = sf.func("my string");
// do whatever ...
}
그리고 이렇게 불릴 것이다:
ref.takingMethod(new StringFunction() {
public int func(String param) {
// body
}
});
편집: Java 8에서는 람다 표현으로 호출할 수 있습니다.
ref.takingMethod(param -> bodyExpression);
각 "함수 포인터"에 대해 계산을 구현하는 작은 펑터 클래스를 만듭니다.모든 클래스에서 구현되는 인터페이스를 정의하고 이러한 객체의 인스턴스를 더 큰 함수로 전달합니다.이는 "명령 패턴"과 "전략 패턴"의 조합입니다.
@sblundy의 예는 좋다.
이 한 줄에서 실행할 수 있는 여러 가지 계산이 미리 정의되어 있는 경우 열거형을 사용하면 전략 패턴을 빠르고 명확하게 구현할 수 있습니다.
public enum Operation {
PLUS {
public double calc(double a, double b) {
return a + b;
}
},
TIMES {
public double calc(double a, double b) {
return a * b;
}
}
...
public abstract double calc(double a, double b);
}
전략 메서드 선언은 물론 각 구현의 인스턴스 1개가 모두 단일 클래스/파일에 정의되어 있습니다.
전달할 기능을 제공하는 인터페이스를 만들어야 합니다.예:
/**
* A simple interface to wrap up a function of one argument.
*
* @author rcreswick
*
*/
public interface Function1<S, T> {
/**
* Evaluates this function on it's arguments.
*
* @param a The first argument.
* @return The result.
*/
public S eval(T a);
}
그런 다음 함수를 전달할 필요가 있는 경우 해당 인터페이스를 구현할 수 있습니다.
List<Integer> result = CollectionUtilities.map(list,
new Function1<Integer, Integer>() {
@Override
public Integer eval(Integer a) {
return a * a;
}
});
마지막으로 맵 함수는 다음과 같이 Function 1에서 전달된 값을 사용합니다.
public static <K,R,S,T> Map<K, R> zipWith(Function2<R,S,T> fn,
Map<K, S> m1, Map<K, T> m2, Map<K, R> results){
Set<K> keySet = new HashSet<K>();
keySet.addAll(m1.keySet());
keySet.addAll(m2.keySet());
results.clear();
for (K key : keySet) {
results.put(key, fn.eval(m1.get(key), m2.get(key)));
}
return results;
}
파라미터를 전달할 필요가 없는 경우에는 Runnable을 자신의 인터페이스 대신 사용할 수 있습니다.또한 파라미터 카운트를 "고정"으로 줄이기 위해 다양한 기술을 사용할 수 있습니다.다만, 통상, 타입의 안전성과의 트레이드 오프입니다(또는, 함수 오브젝트가 그러한 방법으로 파라미터로 통과할 수 있도록 컨스트럭터를 덮어쓸 수도 있습니다).많은 접근법이 있으며, 특정 상황에서 더 잘 작동하는 방법도 있습니다.)
「」를 사용한 ::
메서드가 기능 인터페이스를 받아들이는 메서드 인수에서 메서드 참조를 사용할 수 있습니다.기능 인터페이스는 추상 메서드를 하나만 포함하는 인터페이스입니다(기능 인터페이스에는 기본 메서드 또는 스태틱 메서드를 하나 이상 포함할 수 있습니다).
IntBinaryOperator
는 기능하는 인터페이스입니다.그 추상적인 방법, 는 두 가지를 받아들인다.int
s를 한다.int
.또한 2개의int
및 됩니다.int
예에서는,의A.method(Math::max);
만들다parameter.applyAsInt
을 2로 Math.max
그 .Math.max
.
import java.util.function.IntBinaryOperator;
class A {
static void method(IntBinaryOperator parameter) {
int i = parameter.applyAsInt(7315, 89163);
System.out.println(i);
}
}
import java.lang.Math;
class B {
public static void main(String[] args) {
A.method(Math::max);
}
}
일반적으로 다음을 사용할 수 있습니다.
method1(Class1::method2);
다음 대신:
method1((arg1, arg2) -> Class1.method2(arg1, arg2));
줄임말:
method1(new Interface1() {
int method1(int arg1, int arg2) {
return Class1.method2(arg1, agr2);
}
});
상세한 것에 대하여는, Java 8 및 Java Language Specification © 15.13 의 ::(이중 콜론) 연산자를 참조해 주세요.
이 작업을 수행할 수도 있습니다(희귀한 경우도 있습니다).문제는 클래스/인터페이스를 사용하는 타입의 안전성이 모두 상실되어 메서드가 존재하지 않는 경우에 대처해야 한다는 것입니다.
액세스 제한을 무시하고 프라이빗 메서드를 호출할 수 있다는 장점이 있습니다(이 예에서는 보이지 않지만 컴파일러가 통상 호출을 허용하지 않는 메서드를 호출할 수 있습니다).
다시 말하지만, 이것이 말이 되는 경우는 드물지만, 그럴 때는 좋은 도구입니다.
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
class Main
{
public static void main(final String[] argv)
throws NoSuchMethodException,
IllegalAccessException,
IllegalArgumentException,
InvocationTargetException
{
final String methodName;
final Method method;
final Main main;
main = new Main();
if(argv.length == 0)
{
methodName = "foo";
}
else
{
methodName = "bar";
}
method = Main.class.getDeclaredMethod(methodName, int.class);
main.car(method, 42);
}
private void foo(final int x)
{
System.out.println("foo: " + x);
}
private void bar(final int x)
{
System.out.println("bar: " + x);
}
private void car(final Method method,
final int val)
throws IllegalAccessException,
IllegalArgumentException,
InvocationTargetException
{
method.invoke(this, val);
}
}
다른 회선이1개밖에 없는 경우는 플래그나 if(flag)문 등의 파라미터를 추가하여 어느 회선을 호출할 수 있습니다.
Java 7에서 진행 중인 폐쇄 작업에 대해서도 관심이 있을 수 있습니다.
http://gafter.blogspot.com/2006/08/closures-for-java.html
http://tech.puredanger.com/java7/ #syslog
를 사용한 새로운 Java 8 기능 인터페이스 및 메서드레퍼런스::
환입니니다다
Java 8은 "@ Functional Interface" 포인터를 사용하여 메서드 참조(MyClass::new)를 유지할 수 있습니다.같은 메서드 이름은 필요 없습니다.같은 메서드 서명만 필요합니다.
예:
@FunctionalInterface
interface CallbackHandler{
public void onClick();
}
public class MyClass{
public void doClick1(){System.out.println("doClick1");;}
public void doClick2(){System.out.println("doClick2");}
public CallbackHandler mClickListener = this::doClick;
public static void main(String[] args) {
MyClass myObjectInstance = new MyClass();
CallbackHandler pointer = myObjectInstance::doClick1;
Runnable pointer2 = myObjectInstance::doClick2;
pointer.onClick();
pointer2.run();
}
}
자, 여기 뭐가 있죠?
- Functional Interface(기능 인터페이스) - @Functional(기능)을 사용한 인터페이스 또는 주석 없음1개의 메서드 선언만 포함하는 인터페이스.
- Method References - 이것은 특수한 구문일 뿐이며 다음과 같습니다.인스턴스::methodName, 그 이상도 이하도 아닙니다.
- 사용 예 - 할당 연산자, 인터페이스 메서드 호출만 수행합니다.
기능 인터페이스는 청취자 전용으로 사용해야 합니다.
왜냐하면 다른 모든 함수 포인터는 코드 가독성과 이해 능력에 매우 좋지 않기 때문입니다.단, 직접 메서드 참조는 예를 들어 foreach와 같이 편리한 경우가 있습니다.
기능 인터페이스는 다음과 같이 미리 정의되어 있습니다.
Runnable -> void run( );
Supplier<T> -> T get( );
Consumer<T> -> void accept(T);
Predicate<T> -> boolean test(T);
UnaryOperator<T> -> T apply(T);
BinaryOperator<T,U,R> -> R apply(T, U);
Function<T,R> -> R apply(T);
BiFunction<T,U,R> -> R apply(T, U);
//... and some more of it ...
Callable<V> -> V call() throws Exception;
Readable -> int read(CharBuffer) throws IOException;
AutoCloseable -> void close() throws Exception;
Iterable<T> -> Iterator<T> iterator();
Comparable<T> -> int compareTo(T);
Comparator<T> -> int compare(T,T);
이전 Java 버전에서는 Adrian Petrescu가 언급한 것과 유사한 기능과 구문을 가진 Guava Libraries를 사용해 보십시오.
자세한 내용은 Java 8 Cheatsheet를 참조하십시오.
그리고 The Guy with The Hat for the Java Language Specification © 15.13 링크에 감사드립니다.
@sblundy의 답변은 훌륭하지만 익명의 내부 클래스에는 두 가지 작은 결함이 있습니다. 첫째는 재사용할 수 없는 경향이 있고 둘째는 부피가 큰 구문입니다.
좋은 점은 그의 패턴이 메인 클래스(계산을 수행하는 클래스)에 아무런 변화 없이 풀 클래스로 확장된다는 것이다.
새 클래스를 인스턴스화할 때 방정식의 상수 역할을 할 수 있는 매개 변수를 해당 클래스에 전달할 수 있습니다. 따라서 내부 클래스 중 하나가 다음과 같습니다.
f(x,y)=x*y
하지만 때로는 다음과 같은 것이 필요합니다.
f(x,y)=x*y*2
그리고 아마 3분의 1은 다음과 같습니다.
f(x,y)=x*y/2
2개의 어나니머스 내부 클래스를 만들거나 "패스스루" 파라미터를 추가하는 대신 다음과 같이 인스턴스화하는 단일 EXTAL 클래스를 만들 수 있습니다.
InnerFunc f=new InnerFunc(1.0);// for the first
calculateUsing(f);
f=new InnerFunc(2.0);// for the second
calculateUsing(f);
f=new InnerFunc(0.5);// for the third
calculateUsing(f);
이 명령어는 단순히 클래스에 상수를 저장하고 인터페이스에서 지정된 메서드로 사용합니다.
실제로 기능이 저장/재사용되지 않음을 알고 있다면 다음과 같이 할 수 있습니다.
InnerFunc f=new InnerFunc(1.0);// for the first
calculateUsing(f);
f.setConstant(2.0);
calculateUsing(f);
f.setConstant(0.5);
calculateUsing(f);
하지만 불변의 클래스가 더 안전하다. 나는 이런 클래스를 불변하게 만들 명분을 생각해 낼 수 없다.
익명의 내부 수업을 들을 때마다 움츠러들기 때문에 이 글을 올리는 것뿐입니다.프로그래머가 처음 한 일은 실제 수업을 이용해야 할 때 익명이 되어 자신의 결정을 재고하지 않는 것이었기 때문에, 「필수」라고 하는 중복 코드를 많이 봐 왔습니다.
매우 인기를 끌고 있는 Google Guava 라이브러리는 API의 많은 부분에서 작업한 일반적인 함수 및 술어 개체를 가지고 있습니다.
자바에서 프로그래밍할 때 가장 그리워하는 것 중 하나는 함수 콜백입니다.이러한 요구 자체가 계속 발생하는 한 가지 상황은 각 항목에 대해 특정 작업을 수행하고자 하는 계층 구조를 반복적으로 처리하는 것입니다.디렉토리 트리 워킹이나 데이터 구조 처리 등.내 안의 미니멀리스트는 특정 케이스별로 인터페이스를 정의하고 구현해야 하는 것을 싫어합니다.
어느 날 나는 왜 안 될까 하는 생각이 들었다.메서드 포인터 - 메서드 오브젝트가 있습니다.JIT 컴파일러의 최적화를 통해 반사 호출은 더 이상 큰 성능 저하를 수반하지 않습니다.예를 들어 파일을 한 위치에서 다른 위치로 복사하는 것 외에, 반사된 메서드 호출에 드는 비용은 중요하지 않습니다.
더 생각해보니 OOP 패러다임에서 콜백은 오브젝트와 메서드를 함께 바인딩해야 한다는 것을 깨달았습니다. 즉, 콜백 오브젝트를 입력합니다.
Java의 Callback용 리플렉션 기반 솔루션을 확인하십시오.어떤 사용도 무료입니다.
나한테는 전략 패턴처럼 들리네요fluffycat.com Java 패턴을 확인해 주세요.
네, 이 스레드는 이미 오래되었기 때문에 아마 제 답변은 질문에 도움이 되지 않을 것입니다.하지만 이 스레드가 해결 방법을 찾는 데 도움이 되었기 때문에, 어쨌든 여기에 내 놓을게요.
기존 입력과 기존 출력(둘 다 이중)을 가진 가변 정적 방식을 사용해야 했습니다.그러면 메서드 패키지와 이름을 알고 다음과 같이 작업할 수 있습니다.
java.lang.reflect.Method Function = Class.forName(String classPath).getMethod(String method, Class[] params);
하나의 이중을 모수로 받아들이는 함수의 경우.
그래서, 내 구체적인 상황에서, 나는 그것을 로 초기화했다.
java.lang.reflect.Method Function = Class.forName("be.qan.NN.ActivationFunctions").getMethod("sigmoid", double.class);
나중에 좀 더 복잡한 상황에서 발동해서
return (java.lang.Double)this.Function.invoke(null, args);
java.lang.Object[] args = new java.lang.Object[] {activity};
someOtherFunction() + 234 + (java.lang.Double)Function.invoke(null, args);
여기서 activity는 임의의 이중값입니다.Software Monkey처럼 좀 더 추상적이고 일반화하려고 생각하고 있습니다만, 현재는 충분히 만족하고 있습니다.코드 세 줄이면 클래스나 인터페이스가 필요 없어요.그건 나쁘지 않아요.
일련의 기능에 대해 인터페이스를 사용하지 않고 동일한 작업을 수행하려면 다음 절차를 따릅니다.
class NameFuncPair
{
public String name; // name each func
void f(String x) {} // stub gets overridden
public NameFuncPair(String myName) { this.name = myName; }
}
public class ArrayOfFunctions
{
public static void main(String[] args)
{
final A a = new A();
final B b = new B();
NameFuncPair[] fArray = new NameFuncPair[]
{
new NameFuncPair("A") { @Override void f(String x) { a.g(x); } },
new NameFuncPair("B") { @Override void f(String x) { b.h(x); } },
};
// Go through the whole func list and run the func named "B"
for (NameFuncPair fInstance : fArray)
{
if (fInstance.name.equals("B"))
{
fInstance.f(fInstance.name + "(some args)");
}
}
}
}
class A { void g(String args) { System.out.println(args); } }
class B { void h(String args) { System.out.println(args); } }
lamdaj를 확인
http://code.google.com/p/lambdaj/
특히 새로운 클로징 기능은
http://code.google.com/p/lambdaj/wiki/Closures
의미 없는 인터페이스를 만들거나 추악한 내부 클래스를 사용하지 않고 폐쇄 또는 함수 포인터를 정의하는 매우 읽기 쉬운 방법을 찾을 수 있습니다.
와, java에 대해서는 이미 했으니까 그렇게 어렵지 않은 Delegate 클래스를 만들고 T가 return type인 파라미터를 전달하는데 사용하면 어떨까요?죄송합니다만, 일반적으로 자바를 배우는 C++/C# 프로그래머로서 매우 편리하기 때문에 함수 포인터가 필요합니다.메서드 정보를 다루는 클래스에 익숙한 경우 수행할 수 있습니다.자바 라이브러리에서는 java.lang.reflect.method가 됩니다.
인터페이스를 항상 사용하는 경우는, 항상 실장할 필요가 있습니다.이벤트 처리에서는 핸들러 목록에서 등록/등록을 해제하는 데 더 좋은 방법이 없습니다.대리점에서는 값 유형이 아닌 함수를 전달해야 합니다.대리점 클래스가 인터페이스의 아웃클래스를 처리할 수 있도록 합니다.
Java 8의 답변 중 어느 것도 완전하고 일관성 있는 예를 제시하지 않았습니다.이것을 소개합니다.
다음과 같이 "함수 포인터"를 받아들이는 방법을 선언합니다.
void doCalculation(Function<Integer, String> calculation, int parameter) {
final String result = calculation.apply(parameter);
}
함수에 람다 식을 제공하여 호출합니다.
doCalculation((i) -> i.toString(), 2);
동작을 정의하기 위해 하나의 파라미터 세트를 사용하는 함수와 Scheme와 같이 실행할 다른 파라미터 세트를 사용하는 함수를 전달하기 위해 어려움을 겪고 있는 경우:
(define (function scalar1 scalar2)
(lambda (x) (* x scalar1 scalar2)))
Java에서 매개 변수 정의 동작을 사용한 함수 전달 참조
Java8 이후로는 공식 SE 8 API에 라이브러리가 있는 람다를 사용할 수 있습니다.
사용방법: 인터페이스는 추상적인 방법을 하나만 사용해야 합니다.다음과 같이 인스턴스를 만듭니다(이미 제공된 Java SE 8을 사용하는 것이 좋습니다).
Function<InputType, OutputType> functionname = (inputvariablename) {
...
return outputinstance;
}
상세한 것에 대하여는, 다음의 문서를 참조해 주세요.https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html
Java 8 이전에 함수 포인터와 같은 기능을 가장 가까운 대체물은 익명 클래스였습니다.예를 들어 다음과 같습니다.
Collections.sort(list, new Comparator<CustomClass>(){
public int compare(CustomClass a, CustomClass b)
{
// Logic to compare objects of class CustomClass which returns int as per contract.
}
});
그러나 현재 Java 8에서는 람다 표현이라고 하는 매우 깔끔한 대체 방법이 제공되고 있습니다. 이 방법은 다음과 같이 사용될 수 있습니다.
list.sort((a, b) -> { a.isBiggerThan(b) } );
는 은 Than의 메서드입니다.CustomClass
여기서도 메서드 레퍼런스를 사용할 수 있습니다.
list.sort(MyClass::isBiggerThan);
오픈 소스 안전 미러 프로젝트는 위에서 언급한 솔루션 중 일부를 Java에 기능, 위임 및 이벤트를 추가하는 라이브러리로 일반화합니다.
기능의 치트 시트에 대해서는, README 또는 이 스택 오버 플로우의 회답을 참조해 주세요.
함수에 대해서는 라이브러리에서는 Fun 인터페이스와 (제너릭과 함께) 메서드를 유형으로 사용하기 위한 fluent API를 구성하는 서브 인터페이스를 도입하고 있습니다.
Fun.With0Params<String> myFunctionField = " hello world "::trim;`
Fun.With2Params<Boolean, Object, Object> equals = Objects::equals;`
public void foo(Fun.With1ParamAndVoid<String> printer) throws Exception {
printer.invoke("hello world);
}
public void test(){
foo(System.out::println);
}
주의:
- 대상 시그니처의 파라미터 수와 일치하는 서브 인터페이스를 선택해야 합니다.Fx에 파라미터가 1개 있으면 Fun을 선택합니다.With 1Param.
- A) 리턴 타입 및 B) 시그니처의 파라미터를 정의하기 위해 Generic을 사용합니다.
또한 foo() 메서드로 콜에 전달되는 메서드레퍼런스의 시그니처는 메서드 Foo에서 정의된 Fun과 일치해야 합니다.그렇지 않으면 컴파일러에서 오류가 발생합니다.
언급URL : https://stackoverflow.com/questions/122407/whats-the-nearest-substitute-for-a-function-pointer-in-java
'programing' 카테고리의 다른 글
여러 엔트리를 HashMap에 한 번에 추가, 1개의 스테이트먼트에서 (0) | 2022.07.28 |
---|---|
Vue 및 부트스트랩:미디어 중단점에 따라 다른 컴포넌트를 표시하는 방법최신 컨센서스란? (0) | 2022.07.28 |
Vue 구성 요소가 활성 상태인지 확인하는 방법 (0) | 2022.07.28 |
누가 유니언을 쓰겠어?C-only 시절의 유물인가요? (0) | 2022.07.28 |
String에서 %를 이스케이프하는 방법.포맷? (0) | 2022.07.28 |