스칼라 함수형 프로그래밍 - SAM (Single abstract method)의 이해와 활용 (단일 추상 메소드를 람다 표현식으로 구현할 수 있어요.)
View: 260
0
0
작성자: 달빛제이크
카테고리: Scala Language
발행: 2024-06-17
수정
2024-06-21
안녕하세요. 달빛제이크입니다.
지난 글에서는 함수가 제공하는 특별한 기능으로 Repeated parameters, Named arguments, Default parameter values에 대해서 알아보았습니다.
이번 글에서는 SAM (Single abstract method, 단일 추상 메소드)에 대해서 살펴보겠습니다.
1. SAM (Single Abstract Method, 단일 추상 클래스)
JAVA를 공부하신 분들은 Swing으로 UI Component를 만들 때 addActionListener에 ActionListener를 등록한 경험이 있으실 겁니다.
ActionListener는 actionPerformed라는 한 개의 추상 메소드 만을 가지고 있는 Interface인데, 이 추상 메소드를 overriding해서 구현해주고 바로 addActionListener에 익명클래스로 전달을 해 줍니다.
import javax.swing.JButton;
import javax.swing.JFrame;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class ActionListenerExample {
public static void main(String[] args) {
JFrame frame = new JFrame("ActionListener Example");
JButton button = new JButton("Click Me");
// 익명 클래스를 사용하여 ActionListener 구현
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("버튼 클릭됨! (익명 클래스 사용)");
}
});
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(button);
frame.setSize(300, 200);
frame.setVisible(true);
}
}
갑자기 JAVA Code를 예시로 보여드려서 당황하실 수도, 반가워 하실 수도 있을 것 같습니다. 다만, SAM의 대표적 사용 사례 중 하나가 JAVA의 ActionListener이다 보니 JAVA의 예시로 부터 출발하는 것이 바람직할 것 같습니다.
앞의 예제에서 "Click Me"라는 이름의 JButton을 만들고 이 JButton을 클릭하면 터미널에 "버튼 클릭됨! (익명 클래스 사용)"이라는 메세지가 출력 되도록 코드를 작성했습니다. ActionListener를 addActionListener의 argument 자리에서 바로 actionPerformed라는 메소드를 구현하고 new 키워드로 익명클래스를 만들어 직접 parameter에 전달했습니다.
그렇다면 왜 이런 형식을 사용하는 걸까요? 그것은 JButton의 입장에서 생각해봐야 합니다.
button의 역할은 click을 하면 어떤 일을 수행하는 것입니다. button을 클릭하는 것, 이 하나의 이벤트가 발생하면 일을 수행합니다. JButton class는 이벤트가 발생 했을 때 해야 할 일을 addActionListner 메소드를 통해 전달 받습니다. 넘어온 객체는 이 할 일에 대한 정보를 가지고 있습니다. JButton은 addActionListener에서 받은 객체가 ActionListener라는 것을 알고 있습니다. JButton 만들 때 ActionListener를 전달 받기로 약속했으니까요. 그래서 이름도 addActionListener입니다. 그리고 그 안에 actionPerformed라는 이름을 가진 한 개의 메소드만 있다는 것도 알고 있습니다. 사용자가 button을 클릭하면 전달 받은 객체의 actionPerformed를 수행하도록 Class가 구현되었습니다.
이번에는 JButton에 ActionListener를 전달하는 입장에서 생각해 보겠습니다.
button을 클릭 했을 때 발생하는 동작은 정해져 있지 않습니다. button이 하는 일을 미리 정해놓을 수는 없기 때문에 ActionListener를 전달할 때 수행해야 할 일을 정의하기로 합니다. 그래서 ActionListener의 actionPerformed 는 추상 메소드로 남겨 놓습니다.
단일 메소드이기 때문에 이 메소드 하나만 overriding해서 구현해주면 쉽게 ActionListener를 완성 시킬 수 있습니다. 그리고 ActionListener는 Data를 가지고 있는 객체가 아니기 때문에 굳이 변수를 할당해서 메모리에 저장할 필요가 없을 뿐더러, UI를 구현하기 위해서 JButton 외에도 다른 다양한 component들을 만들고 이벤트들을 등록해주어야 하기 때문에 익명클래스로 작성해서 바로 addActionListener에 넘겨 주기로 합니다.
SAM (Single abstract method, 단일 추상 메소드)은 말 그대로 인터페이스 내부에 한 개만 존재하는 추상 메소드를 의미하고, SAM Interface는 하나의 추상 메소드 만을 가지고 있는 인터페이스 입니다. SAM을 선언하기 위해서는 당연히 인터페이스 형식일 수 밖에 없는 것이죠. 그리고 인터페이스라는 형식이 있기 때문에 굳이 추상 클래스로 선언할 필요가 없습니다.
2. 스칼라 (Scala) 언어에서 SAM (Single Abstract Method, 단일 추상 메소드)
자바8 부터 SAM Interface를 전달 할 때 람다 표현식을 사용할 수 있게 Update 되었습니다. 코드 작성이 매우 간단해졌습니다.
스칼라에서도 SAM으로 구현된 trait를 작성해서 전달하거나, Function literal을 사용할 수 있습니다.
예제를 통해 확인해 보겠습니다.
// SAM 선언
trait Increaser:
def increase(i: Int): Int
// SAM trait을 인자로 받는 메소드 작성
def increaseOne(increaser: Increaser): Int =
increaser.increase(1)
// SAM trait를 구현해서 increaseOne 인자로 전달
increaseOne(
new Increase:
def increase(i: Int): Int = i + 7
)
// Function literal (함수 리터럴)을 인자로 전달
increaseOne( i => i + 7)
자바의 인터페이스를 트레이트로 구현했습니다. SAM trait를 선언하고, 이 trait를 사용하는 increaseOne이라는 메소드를 작성했습니다. 이 메소드는 SAM에 1이라는 인자를 전달합니다. Increaser의 increase라는 추상 메소드가 어떤 일을 하든지 1이라는 값을 전달 해주기로 합니다. SAM trait를 increaseOne의 argument 자리에 직접 구현해서 인자로 넘겨 줍니다. increase의 parameter에 어떤 값이 넘어오든 7을 더하는 일을 정의했습니다. 종합해 보면 increaseOne에서 Increaser.increase 메소드에 1을 넘겨 주었고 increase 메소드는 7을 더하기로 했으니 결국 8이 되는 일을 하게 됩니다. 마지막 예제에서는 함수 리터럴을 사용해서 간단하게 할 일을 정의해 주었습니다. 코드가 매우 간결해졌습니다.
SAM Type은 콜백, 이벤트 처리, 스레드 작업 등을 처리할 때 주로 사용합니다. 이러한 기능들은 단일 메소드를 필요로 하고, Task가 목적에 따라 달라질 수 있습니다.
이번 글에서는 SAM (Single abstract Method)에 대해서 알아 보았습니다.
SAM Type에 함수 리터럴을 사용하면 코드가 매우 간결해지고 가독성을 높일 수 있습니다.
다음 글에서는 함수형 프로그래밍 방식에서 많이 사용하는 Tail recursion (꼬리 재귀 함수)에 대해서 이야기하겠습니다.
감사합니다.
