자바 8 람다 무효 인수


188

Java 8에서 다음과 같은 기능 인터페이스가 있다고 가정 해 봅시다.

interface Action<T, U> {
   U execute(T t);
}

그리고 어떤 경우에는 인수 또는 반환 유형이없는 작업이 필요합니다. 그래서 다음과 같이 씁니다.

Action<Void, Void> a = () -> { System.out.println("Do nothing!"); };

그러나 컴파일 오류가 발생하므로 다음과 같이 작성해야합니다.

Action<Void, Void> a = (Void v) -> { System.out.println("Do nothing!"); return null;};

못생긴 것입니다. Voidtype 매개 변수를 제거하는 방법이 있습니까?



7
정의한대로 조치가 필요한 경우 불가능합니다. 그러나 첫 번째 예는에 들어갈 수있는 Runnable당신을 위해 무엇을 찾고있는Runnable r = () -> System.out.println("Do nothing!");
알렉시스 C.

1
@BobTheBuilder 그 게시물에서 제안한 것처럼 소비자를 사용하고 싶지 않습니다.
Wickoo

2
Matt의 대답으로 유형이 작동하지만 null 반환 값을 얻을 때 호출자는 어떻게합니까?
스튜어트 마크

8
당신은 손가락을 엇갈리게 하고이 게시물의 제안 2 & 3 Java 9에 적용 되기를 바랍니다 !
assylias

답변:


110

다음 구문은 a Runnable로 변환하는 작은 도우미 함수로 가능합니다 Action<Void, Void>( Action예 : 배치 할 수 있음 ).

public static Action<Void, Void> action(Runnable runnable) {
    return (v) -> {
        runnable.run();
        return null;
    };
}

// Somewhere else in your code
 Action<Void, Void> action = action(() -> System.out.println("foo"));

4
이것은 당신이 얻을 수있는 가장 깨끗한 해결 방법입니다. IMO, +1 (또는 인터페이스 자체에 정적 메소드 사용)
Alexis C.

아래의 Konstantin Yovkov 솔루션 (@FunctionalInterface 포함)은 제네릭을 포함하지 않고 추가 코드가 필요하지 않기 때문에 더 나은 솔루션입니다.
uthomas

@uthomas 죄송합니다 @FunctionalInterface.에 관련된 답변이 없습니다 . 그는 단지 그것을 확장 할 수 없다고 말합니다 ...
Matt

1
@Matt 님, 안녕하세요. 죄송합니다. 나는 너무 빨리 반응했다. 주어진 질문에 대해 당신은 대답이 완벽하게 유효합니다. 불행히도, 내 투표가 잠겨 있으므로이 답변에서 -1을 제거 할 수 없습니다. 두 가지주의 사항 : 1. Runnable행동 대신에 , @FunctionalInterface라는 커스텀 무언가를 취해야합니다 SideEffect. 2. 그러한 도우미 기능의 필요성은 이상한 일이 일어나고 있으며 아마도 추상화가 깨 졌음을 강조합니다.
uthomas

530

Supplier아무것도 걸리지 않지만 무언가를 반환하면 사용하십시오 .

Consumer무언가가 필요하지만 아무것도 반환하지 않으면 사용하십시오 .

Callable결과를 반환하고 던질 수있는 경우에 사용하십시오 ( Thunk일반적인 CS 용어 와 유사 ).

Runnable던지지 않고 던질 수없는 경우 사용하십시오 .


예를 들어 "void"return call을 랩핑하기 위해이 작업을 수행했습니다 public static void wrapCall(Runnable r) { r.run(); }. 감사합니다
Maxence

13
아름다운 대답. 짧고 정확합니다.
클린트 이스트우드

불행히도 확인 된 예외를 던져야하는 경우 도움이되지 않습니다.
Jesse Glick

13
이 답변의 완성으로 편집 할 가치가 없습니다 : BiConsumer (2를 받아 0을 반환), Function (1을 받아 1을 반환) 및 BiFunction (2를 받아 1을 반환)을 사용할 수도 있습니다. 가장 알아야 할 것들
CLOVIS

2
Callable (call () 메서드에서 예외를 발생시키는)과 같은 것이 있지만 반환 값이 필요합니까?
dpelisek

40

람다 :

() -> { System.out.println("Do nothing!"); };

실제로 다음과 같은 인터페이스에 대한 구현을 나타냅니다.

public interface Something {
    void action();
}

정의한 것과는 완전히 다릅니다. 그래서 오류가 발생합니다.

를 확장하거나 @FunctionalInterface새로운 것을 소개 할 수 없으므로 많은 옵션이 없다고 생각합니다. Optional<T>인터페이스를 사용하여 일부 값 (반환 유형 또는 메소드 매개 변수)이 누락되었음을 표시 할 수 있습니다 . 그러나 이것은 람다 본문을 더 단순하게 만들지는 않습니다.


문제는 Something함수가 내 Action유형 의 하위 유형이 될 수 없으며 두 가지 유형을 가질 수 없다는 것입니다.
Wickoo

기술적으로 그는 할 수 있지만 그는 피하고 싶다고 말했다. :)
Konstantin Yovkov

31

해당 특수한 경우에 대한 하위 인터페이스를 만들 수 있습니다.

interface Command extends Action<Void, Void> {
  default Void execute(Void v) {
    execute();
    return null;
  }
  void execute();
}

기본 메서드 를 사용 하여 상속 된 매개 변수화 된 메서드 를 재정 Void execute(Void)의하여 더 간단한 메서드에 대한 호출을 위임합니다 void execute().

결과적으로 사용이 훨씬 간단 해집니다.

Command c = () -> System.out.println("Do nothing!");

이 행동 <Void, Void>는 어디에서 오는가? Swing이나 JAX-WX Action 인터페이스에는 일반적인 인터페이스가 없습니까?
luis.espinal

1
@ luis.espinal : Action<T, U>질문에 선언됩니다 .....
Jordão

하하, 내가 어떻게 그리워 했어? 감사!
luis.espinal

6

이 테이블이 짧고 유용하다고 생각합니다.

Supplier       ()    -> x
Consumer       x     -> ()
Callable       ()    -> x throws ex
Runnable       ()    -> ()
Function       x     -> y
BiFunction     x,y   -> z
Predicate      x     -> boolean
UnaryOperator  x1    -> x2
BinaryOperator x1,x2 -> x3

다른 답변에서 언급 했듯이이 문제에 대한 적절한 옵션은 Runnable


5

불가능합니다. 무효가 아닌 반환 유형을 가진 함수 Void는 값을 반환해야합니다. 그러나 정적 메소드를 추가하여 Action다음과 같이 "생성"할 수 있습니다 Action.

interface Action<T, U> {
   U execute(T t);

   public static Action<Void, Void> create(Runnable r) {
       return (t) -> {r.run(); return null;};
   }

   public static <T, U> Action<T, U> create(Action<T, U> action) {
       return action;
   } 
}

그러면 다음을 쓸 수 있습니다.

// create action from Runnable
Action.create(()-> System.out.println("Hello World")).execute(null);
// create normal action
System.out.println(Action.create((Integer i) -> "number: " + i).execute(100));

4

기능적 인터페이스 내에 정적 메소드 추가

package example;

interface Action<T, U> {
       U execute(T t);
       static  Action<Void,Void> invoke(Runnable runnable){
           return (v) -> {
               runnable.run();
                return null;
            };         
       }
    }

public class Lambda {


    public static void main(String[] args) {

        Action<Void, Void> a = Action.invoke(() -> System.out.println("Do nothing!"));
        Void t = null;
        a.execute(t);
    }

}

산출

Do nothing!

3

함수 정의가 예제에서 일치하지 않기 때문에 가능하지 않다고 생각합니다.

람다 식은 다음과 같이 정확하게 평가됩니다

void action() { }

당신의 선언은 다음과 같습니다

Void action(Void v) {
    //must return Void type.
}

예를 들어 다음과 같은 인터페이스가있는 경우

public interface VoidInterface {
    public Void action(Void v);
}

호환되는 유일한 기능 (인스턴스화 중)은 다음과 같습니다.

new VoidInterface() {
    public Void action(Void v) {
        //do something
        return v;
    }
}

return 문이나 인수가 없으면 컴파일러 오류가 발생합니다.

따라서 인수를 가져 와서 반환하는 함수를 선언하면 위에서 언급하지 않은 함수로 변환 할 수 없다고 생각합니다.


3

메소드가 값을 던지거나 리턴하는 경우 메소드 참조에 사용할 수있는 기능 인터페이스를 참조하십시오.

void notReturnsNotThrows() {};
void notReturnsThrows() throws Exception {}
String returnsNotThrows() { return ""; }
String returnsThrows() throws Exception { return ""; }

{
    Runnable r1 = this::notReturnsNotThrows; //ok
    Runnable r2 = this::notReturnsThrows; //error
    Runnable r3 = this::returnsNotThrows; //ok
    Runnable r4 = this::returnsThrows; //error

    Callable c1 = this::notReturnsNotThrows; //error
    Callable c2 = this::notReturnsThrows; //error
    Callable c3 = this::returnsNotThrows; //ok
    Callable c4 = this::returnsThrows; //ok

}


interface VoidCallableExtendsCallable extends Callable<Void> {
    @Override
    Void call() throws Exception;
}

interface VoidCallable {
    void call() throws Exception;
}

{
    VoidCallableExtendsCallable vcec1 = this::notReturnsNotThrows; //error
    VoidCallableExtendsCallable vcec2 = this::notReturnsThrows; //error
    VoidCallableExtendsCallable vcec3 = this::returnsNotThrows; //error
    VoidCallableExtendsCallable vcec4 = this::returnsThrows; //error

    VoidCallable vc1 = this::notReturnsNotThrows; //ok
    VoidCallable vc2 = this::notReturnsThrows; //ok
    VoidCallable vc3 = this::returnsNotThrows; //ok
    VoidCallable vc4 = this::returnsThrows; //ok
}

더 많은 컨텍스트를 추가하십시오. 이것은 흥미로워 보이지만 그 의미는 즉시 명확하지 않습니다.
bnieland
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.