티스토리 뷰

다른 클래스(B)에서 어떤 작업을 할 때 특정 작업이 끝난 이후에 동작을 외부(A)에서 설정하고자 할 때가 있을 것이다.


B에 A에서 필요한 메소드를 정의하고 내용은 전부 인수로 넘겨주는 방법을 활용하는 경우가 있다.


하지만 Thread와 같이 비동기 작업을 하면서 특정 작업을 기다린 후에 어떤 동작을 하고자 한다면 어떡해야할까?


흔히 사용되는 방법이 Listener를 등록하는 방법이다.

public class Main {
    public static void main(String... args) {
        TestThread testThread = new TestThread();
        testThread.setOnThreadFinishedListener(() -> System.out.println("Thread finished."));
        testThread.start();
    }
}

public class TestThread extends Thread {
    private OnThreadFinishedListener onThreadFinishedListener;

    public void setOnThreadFinishedListener(OnThreadFinishedListener onThreadFinishedListener) {
        this.onThreadFinishedListener = onThreadFinishedListener;
    }

    @Override
    public void run() {
        doStuff();

        if (onThreadFinishedListener != null)
            onThreadFinishedListener.onThreadFinished();
    }

    private void doStuff() {
        System.out.println("TestThread.doStuff");
    }

    @FunctionalInterface
    public interface OnThreadFinishedListener {
        void onThreadFinished();
    }
}

끝났을 때의 작업을 미리 원하는 작업으로 등록할 수 있다.


OnThreadFinishedListener는 @FuctionalInterface라는 Annotation을 달아놓아 하나의 메소드만 선언할 수 있도록 강제된다.

동작을 넘기고자 하는 것인데, 당연히도 여러 동작을 넘길 수는 없을 것이다. 여러 Listener를 인수로 받아 set할 수는 있겠지만 보기 좋은 방법은 아닐 것이다.

굳이 여러 메소드를 넘기고자 한다면 Java 7이전에 사용했든 익명 클래스를 사용하면 된다.



실행결과:

  TestThread.doStuff

  Thread finished. 


조금 더 예를 들어 DataInputStream으로부터 readUTF()를 기다린 후의 작업을 한다고 생각해보자.


public class Main {
    public static void main(String... args) throws IOException {
        TestThread testThread = new TestThread();
        testThread.setOnRespondListener(System.out::println);
        testThread.start();
    }
}

public class TestThread extends Thread { private static final String HOST_ADDRESS = ""; private static final int HOST_PORT = 0; private Socket socket; private DataInputStream dataInputStream; private OnRespondListener onRespondListener; public TestThread() throws IOException { socket = new Socket(HOST_ADDRESS, HOST_PORT); dataInputStream = new DataInputStream(socket.getInputStream()); } public void setOnRespondListener(OnRespondListener onRespondListener) { this.onRespondListener = onRespondListener; } @Override public void run() { try { String response = dataInputStream.readUTF(); if (onRespondListener != null) onRespondListener.onRespond(response); } catch (IOException e) { e.printStackTrace(); } } @FunctionalInterface public interface OnRespondListener { void onRespond(String response); } }

제법 그럴싸한 모양의 Listener가 등록되었다.

이번엔 Lambda를 이용하지 않고 인수가 일치할 때 Method를 통째로 넘길 수 있는 Method reference가 사용되었다.


하지만 Lambda 또는 Method reference를 이용한 방법은 Java 8이상에서만 지원된다.


이전 버전의 Java에서 사용하는 방법은 익명 클래스(Anonymous class)를 사용하는 방법이다.


public class Main {
    public static void main(String... args) throws IOException {
        TestThread testThread = new TestThread();
        testThread.setOnRespondListener(new TestThread.OnRespondListener() {
            @Override
            public void onRespond(String response) {
                System.out.println(response);
            }
        });
        testThread.start();
    }
}

public class TestThread extends Thread {
    private static final String HOST_ADDRESS = "";
    private static final int HOST_PORT = 0;

    private Socket socket;
    private DataInputStream dataInputStream;

    private OnRespondListener onRespondListener;

    public TestThread() throws IOException {
        socket = new Socket(HOST_ADDRESS, HOST_PORT);
        dataInputStream = new DataInputStream(socket.getInputStream());
    }

    public void setOnRespondListener(OnRespondListener onRespondListener) {
        this.onRespondListener = onRespondListener;
    }

    @Override
    public void run() {
        try {
            String response = dataInputStream.readUTF();

            if (onRespondListener != null)
                onRespondListener.onRespond(response);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public interface OnRespondListener {
        void onRespond(String response);
    }
}

이 경우 보기도 썩 좋진 않지만 여전히 동작을 전달한다는 기능에는 충실하다.

또 다른 차이점은, OnRespondListener의 @FunctionalInterface Annotation을 사용할 수 없다는 점이다.

당연하지만 Funtional interface자체가 Java 8에 나온 개념이니 문제될 것은 없다.



'Java' 카테고리의 다른 글

Functional interface를 이용한 메소드 전달  (0) 2018.06.02
댓글
댓글쓰기 폼