Java는 메모장, 그림판, 계산기, Eclipse와 같은 데스크톱 애플리케이션(CS 프로그램) 개발을 위해 설계되었다.
1. AWT (Abstract Window Toolkit)
2. Swing
import java.awt.*;
import java.awt.event.*;
// 커스텀 프레임 클래스
class MyFrame extends Frame {
public MyFrame(String title) {
super(title); // 부모 클래스(Frame)의 생성자 호출
}
}
// 이벤트 핸들러 클래스 (재사용 가능하지만 일반적으로 재사용성 낮음)
class BtnClickHandler implements ActionListener {
private TextField txtId; // ❌ 필드 선언 필요
private TextField txtPwd; // ❌ 필드 선언 필요
// 생성자로 외부 객체를 '전달받아야' 함
public BtnClickHandler(TextField txtId, TextField txtPwd) {
this.txtId = txtId; // 참조를 저장
this.txtPwd = txtPwd; // 참조를 저장
}
@Override
public void actionPerformed(ActionEvent event) {
System.out.println("나 눌렀어!");
}
}
// 메인 클래스
public class AwtFrame {
public static void main(String[] args) {
// 프레임 생성 및 설정
MyFrame my = new MyFrame("Login");
my.setSize(350, 350);
my.setLayout(new FlowLayout());
my.setVisible(true);
// 버튼 컴포넌트 생성
Button btn1 = new Button("첫 번째 버튼");
Button btn2 = new Button("두 번째 버튼");
Button btn3 = new Button("세 번째 버튼");
// 이벤트 감지 및 연결
BtnClickHandler handler = new BtnClickHandler(txtId, txtPwd);
btn1.addActionListener(handler);
// 프레임에 버튼 추가
my.add(btn1);
my.add(btn2);
my.add(btn3);
}
}
접근 방식:
┌─────────────────┐
│ LoginForm │
│ - txtId │───┐
│ - txtPwd │ │ 생성자로 전달
└─────────────────┘ │
↓
┌─────────────────────────────┐
│ BtnHandler (별도 클래스) │
│ - txtId (전달받은 참조) │
│ - txtPwd (전달받은 참조) │
│ + BtnHandler(txtId, txtPwd)│
└─────────────────────────────┘
문제점:
import java.awt.*;
import java.awt.event.*;
class LoginForm2 extends Frame {
Label lblId, lblPwd;
TextField txtId, txtPwd; // ← Outer 클래스의 멤버
Button btnOk;
public LoginForm2(String title) {
super(title);
// 컴포넌트 생성
lblId = new Label("ID:", Label.RIGHT);
lblPwd = new Label("pwd:", Label.RIGHT);
txtId = new TextField(10);
txtPwd = new TextField(10);
txtPwd.setEchoChar('#'); // 비밀번호 마스킹
btnOk = new Button("login");
// 레이아웃 설정
this.setLayout(new FlowLayout());
this.setSize(500, 100);
this.setVisible(true);
// 컴포넌트 추가
this.add(lblId);
this.add(txtId);
this.add(lblPwd);
this.add(txtPwd);
this.add(btnOk);
// ===== Inner Class 활용 =====
// 로컬 내부 클래스 (Local Inner Class)
class BtnHandler implements ActionListener {
// 필드 선언 불필요
// 생성자 불필요
// 장점: Outer Class의 필드에 직접 접근 가능
@Override
public void actionPerformed(ActionEvent e) {
// txtId, txtPwd에 바로 접근 가능 (외부 클래스 멤버)
// Outer 클래스의 txtId를 '직접 조회'
String id = txtId.getText().trim();
String pwd = txtPwd.getText();
System.out.println(e.getSource());
if (id.equals("hong")) {
System.out.println("방가 : " + id + " / " + pwd);
} else {
System.out.println("배고픈 당신은 누구?");
}
}
}
// 이벤트 리스너 등록 - 전달 없이 그냥 생성
btnOk.addActionListener(new BtnHandler());
// ===== 윈도우 닫기 이벤트 처리 =====
this.addWindowListener(new WindowListener() {
@Override
public void windowClosing(WindowEvent e) {
e.getWindow().setVisible(false);
e.getWindow().dispose(); // 메모리 해제
}
// 나머지 메서드들은 사용하지 않지만 구현 필수 (인터페이스)
@Override
public void windowOpened(WindowEvent e) {}
@Override
public void windowClosed(WindowEvent e) {}
@Override
public void windowIconified(WindowEvent e) {}
@Override
public void windowDeiconified(WindowEvent e) {}
@Override
public void windowActivated(WindowEvent e) {}
@Override
public void windowDeactivated(WindowEvent e) {}
});
}
}
public class Ex16_Button_Event_InnerClass {
public static void main(String[] args) {
LoginForm2 login = new LoginForm2("inner class");
}
}
접근 방식:
┌──────────────────────────────┐
│ LoginForm2 │
│ - txtId │←─┐
│ - txtPwd │ │
│ ┌────────────────────────┐ │ │
│ │ BtnHandler (Inner) │ │ │
│ │ (필드 없음) │ │ │ 직접 조회
│ │ actionPerformed() { │ │ │
│ │ txtId.getText() ────┼─┘ │
│ │ txtPwd.getText() ───┼────┘
│ │ } │ │
│ └────────────────────────┘ │
└──────────────────────────────┘
장점:
왜 직접 조회가 가능한가?
this.txtId 같은 참조 없이도 바로 사용import java.awt.*;
import java.awt.event.*;
class LoginForm3 extends Frame {
Label lblId, lblPwd;
TextField txtId, txtPwd;
Button btnOk;
public LoginForm3(String title) {
super(title);
// 컴포넌트 초기화
lblId = new Label("ID:", Label.RIGHT);
lblPwd = new Label("pwd:", Label.RIGHT);
txtId = new TextField(10);
txtPwd = new TextField(10);
txtPwd.setEchoChar('#');
btnOk = new Button("login");
// 레이아웃 설정
this.setLayout(new FlowLayout());
this.setSize(500, 100);
this.setVisible(true);
// 컴포넌트 추가
this.add(lblId);
this.add(txtId);
this.add(lblPwd);
this.add(txtPwd);
this.add(btnOk);
// ✨ 최종: 익명 클래스 (Anonymous Inner Class)
// 클래스명 없이 즉석에서 구현
btnOk.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String id = txtId.getText().trim(); // 직접 조회
String pwd = txtPwd.getText(); // 직접 조회
System.out.println(e.getSource());
if (id.equals("hong")) {
System.out.println("방가 : " + id + " / " + pwd);
} else {
System.out.println("배고픈 당신은 누구?");
}
}
});
// ✨ WindowAdapter 사용 (추상 클래스)
// 필요한 메서드만 오버라이드
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
e.getWindow().setVisible(false);
e.getWindow().dispose(); // 메모리 해제
}
});
}
}
public class Ex17_Button_Event_Final {
public static void main(String[] args) {
LoginForm3 login = new LoginForm3("로그인");
}
}
1. 클래스 이름 불필요
// 이전: 클래스명 필요
class BtnHandler implements ActionListener { ... }
new BtnHandler();
// 익명: 클래스명 불필요
new ActionListener() { ... }
2. 즉석에서 구현
btnOk.addActionListener(new ActionListener() {
// 여기서 바로 구현!
@Override
public void actionPerformed(ActionEvent e) {
// 로직
}
});
3. 코드가 한 곳에 모임
4. 일회성 구현에 최적
this.addWindowListener(new WindowListener() {
// 🔴 7개 메서드 전부 구현 강제!
@Override
public void windowOpened(WindowEvent e) {} // 안 쓰는데 구현
@Override
public void windowClosed(WindowEvent e) {} // 안 쓰는데 구현
@Override
public void windowIconified(WindowEvent e) {} // 안 쓰는데 구현
@Override
public void windowDeiconified(WindowEvent e) {} // 안 쓰는데 구현
@Override
public void windowActivated(WindowEvent e) {} // 안 쓰는데 구현
@Override
public void windowDeactivated(WindowEvent e) {} // 안 쓰는데 구현
@Override
public void windowClosing(WindowEvent e) {
// 그러나 실제로 사용하는 메서드는 이것뿐!
e.getWindow().dispose();
}
});
단점:
this.addWindowListener(new WindowAdapter() {
// ✨ 필요한 메서드만 오버라이드!
@Override
public void windowClosing(WindowEvent e) {
e.getWindow().setVisible(false);
e.getWindow().dispose();
}
// 나머지 6개 메서드는 구현 안 해도 됨!
});
장점:
// WindowListener 인터페이스 (7개 추상 메서드)
interface WindowListener {
void windowOpened(WindowEvent e);
void windowClosed(WindowEvent e);
void windowClosing(WindowEvent e);
void windowIconified(WindowEvent e);
void windowDeiconified(WindowEvent e);
void windowActivated(WindowEvent e);
void windowDeactivated(WindowEvent e);
}
// WindowAdapter 추상 클래스 (모든 메서드를 빈 구현)
abstract class WindowAdapter implements WindowListener {
public void windowOpened(WindowEvent e) {} // 빈 구현
public void windowClosed(WindowEvent e) {} // 빈 구현
public void windowClosing(WindowEvent e) {} // 빈 구현
public void windowIconified(WindowEvent e) {} // 빈 구현
public void windowDeiconified(WindowEvent e) {} // 빈 구현
public void windowActivated(WindowEvent e) {} // 빈 구현
public void windowDeactivated(WindowEvent e) {} // 빈 구현
}
// 사용 시: 필요한 것만 오버라이드
new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
// 이것만 구현하면 됨!
}
}
// 1단계: 별도 클래스 → 생성자로 전달
BtnHandler handler = new BtnHandler(txtId, txtPwd);
btn.addActionListener(handler);
// 2단계: Local Inner Class → 직접 조회
class BtnHandler implements ActionListener { ... }
btn.addActionListener(new BtnHandler());
// 3단계: 익명 클래스 → 즉석 구현 ✨
btn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// 구현
}
});
class BtnHandler implements ActionListener {
private TextField txtId, txtPwd; // 필드 선언
public BtnHandler(TextField txtId, TextField txtPwd) { // 생성자
this.txtId = txtId;
this.txtPwd = txtPwd;
}
public void actionPerformed(ActionEvent e) {
txtId.getText(); // 저장된 참조 사용
}
}
btn.addActionListener(new BtnHandler(txtId, txtPwd));
특징: 재사용 가능, 코드 분리, 생성자 전달 필요
class BtnHandler implements ActionListener {
// 필드 없음, 생성자 없음
public void actionPerformed(ActionEvent e) {
txtId.getText(); // 직접 조회
}
}
btn.addActionListener(new BtnHandler());
특징: 직접 조회, 클래스명 필요, 한 단계 간소화
btn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
txtId.getText(); // 직접 조회
}
});
특징: 클래스명 불필요, 즉석 구현, 군더더기 없음
| 구분 | 별도 클래스 | Inner Class | 익명 클래스 |
|---|---|---|---|
| 접근 방법 | 생성자로 참조 전달 | Outer 멤버 직접 조회 | Outer 멤버 직접 조회 |
| 필드 선언 | 필요 | 불필요 | 불필요 |
| 생성자 | 필요 (매개변수 전달) | 불필요 | 불필요 |
| 클래스명 | 필요 | 필요 | 불필요 ✨ |
| 코드량 | 많음 | 중간 | 적음 |
| 재사용성 | 높음 | 낮음 | 없음 (일회용) |
| 결합도 | 낮음 (독립적) | 높음 (Outer 의존) | 높음 (Outer 의존) |
| 가독성 | 분산 | 집중 | 매우 집중 ✨ |
| 사용처 | 재사용 가능한 핸들러 | 해당 클래스 전용 | 일회성 이벤트 |
new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// 구현
}
}
new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
// 구현
}
}
new Thread() {
@Override
public void run() {
// 구현
}
}
actionPerformed(ActionEvent e) 메서드 구현 필수getText(): 입력값 가져오기setEchoChar(): 비밀번호 등의 입력값을 특정 문자로 마스킹add() 순서대로 화면에 표시됨익명 클래스를 더욱 간결하게 표현할 수 있다.
btnOk.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String id = txtId.getText().trim();
System.out.println(id);
}
});
btnOk.addActionListener(e -> {
String id = txtId.getText().trim();
System.out.println(id);
});
// 한 줄이면 중괄호 생략 가능
btnOk.addActionListener(e -> System.out.println(txtId.getText()));