본문 바로가기

Java

반응형

 

✅ 1. 중첩 클래스 종류 및 특징

유형 설명 선언 위치 접근
인스턴스 멤버 클래스 외부 클래스의 인스턴스 멤버로 선언 클래스 내부 외부 클래스 인스턴스 필요
정적 멤버 클래스 (static) 정적 멤버로 선언됨 클래스 내부 외부 클래스 인스턴스 없이 접근
로컬 클래스 생성자나 메서드 내부에 정의 메서드/생성자 내부 해당 블록 내에서만 사용 가능
익명 클래스 클래스 선언 없이 즉석에서 구현 인스턴스 생성과 동시에 구현 일회용 용도로 사용

✅ 2. 내부 클래스 주요 예시

🧩 인스턴스 멤버 클래스

A.B b = a.new B();

🧩 정적 멤버 클래스

A.B b = new A.B();  // 외부 클래스 인스턴스 없이 사용 가능

🧩 로컬 클래스

void method() {
    class B { ... }
    B b = new B();
}

🧩 this vs A.this

this.field         // 내부 클래스의 필드
A.this.field       // 외부 클래스의 필드

✅ 3. 중첩 인터페이스와 이벤트 리스너 구현

  • 내부에서 인터페이스 정의 후 콜백 형태로 사용
  • 실제 예시: Button.ClickListener 구현
btn.setClickListener(new ClickListener() {
    public void onClick() { ... }
});

✅ 4. 익명 구현 객체 실습 (Home, RemoteControl)

  • 인터페이스를 구현하면서 객체 이름 없이 직접 생성
RemoteControl rc = new RemoteControl() {
    public void turnOn() { ... }
    public void turnOff() { ... }
};

🧩 사용 위치별 예시

위치 동작 예시
필드 TV 제어
메서드 내부 에어컨 제어
메서드 매개변수 난방 제어

🧠 19일차 핵심 요약

개념 설명
중첩 클래스 클래스 내부에 선언된 클래스 (멤버, 정적, 로컬, 익명)
접근 규칙 정적 클래스는 static 멤버만 접근 가능
익명 구현 객체 인터페이스나 추상 클래스의 일회성 구현
this / A.this 내부/외부 클래스 멤버 구분
UI 이벤트 처리 중첩 인터페이스를 활용한 리스너 등록 방식
댓글
반응형

이전 글에서는 람다식이 추상 메서드가 1개인 인터페이스에서만 가능하다는 점을 배웠습니다.
그렇다면 켜기/끄기 같은 2가지 동작을 동시에 람다식으로 표현하고 싶을 때는 어떻게 해야 할까요?

이번 글에서는 기능을 역할별로 나누어 인터페이스를 분리하고,
각각을 람다식으로 처리하는 방식으로 문제를 해결해봅니다.


📦 동작별 인터페이스 정의

package ch09.sec07.test;

public interface PowerOn {
	void run();  // 켜기 동작
}
package ch09.sec07.test;

public interface PowerOff {
	void run();  // 끄기 동작
}

✔ 두 개의 동작을 각각 함수형 인터페이스로 분리!


📦 람다식 적용 클래스 (RemoteControl.java)

package ch09.sec07.test;

public class RemoteControl {

	private PowerOn on = () -> {
		System.out.println("TV를 켭니다.");
	};

	private PowerOff off = () -> {
		System.out.println("TV를 끕니다.");
	};

	public void use1() {
		on.run();
		off.run();
	}

	public void use2() {
		PowerOn on = () -> {
			System.out.println("에어컨을 켭니다.");
		};

		PowerOff off = () -> {
			System.out.println("에어컨을 끕니다.");
		};

		on.run();
		off.run();
	}

	public void use3(PowerOn on, PowerOff off) {
		on.run();
		off.run();
	}
}

📌 실행 클래스 (HomeExample.java)

package ch09.sec07.test;

public class HomeExample {
	public static void main(String[] args) {
		RemoteControl rc = new RemoteControl();

		rc.use1(); // TV
		rc.use2(); // 에어컨
		rc.use3(
			() -> System.out.println("난방을 켭니다."),
			() -> System.out.println("난방을 끕니다.")
		);
	}
}

💻 실행 결과

TV를 켭니다.
TV를 끕니다.
에어컨을 켭니다.
에어컨을 끕니다.
난방을 켭니다.
난방을 끕니다.

💬 코드 설명

메서드 인터페이스
use1() 필드에 선언된 람다
use2() 지역 변수 람다
use3() 매개변수로 받은 람다

💡 포인트 정리

  • 여러 개의 동작이 필요할 때는 인터페이스를 역할별로 분리
  • 각 인터페이스는 @FunctionalInterface가 되도록 메서드는 하나만
  • 분리된 인터페이스는 각각 람다식으로 구현 가능
  • 코드 간결성 + 역할 분리 → 유지 보수도 쉬움

📌 정리하자면,
기능이 여러 개인 인터페이스는 람다식 사용이 제한되지만,
기능을 나누어 인터페이스를 설계하면 오히려 더 명확하고 유연한 코드를 만들 수 있습니다.

댓글
반응형

이번 글에서는 인터페이스를 직접 구현하지 않고, 익명 객체를 사용해 동작을 정의하는 방법을 학습합니다.
이 방식은 코드를 간결하게 만들고, 일회성 기능 구현이 가능하여
UI 이벤트, 기기 제어, 콜백 처리 등에 널리 사용됩니다.


📦 인터페이스 정의 (RemoteControl.java)

package ch09.sec07;

public interface RemoteControl {
	void turnOn();
	void turnOff();
}

📦 익명 구현 객체를 활용한 클래스 (Home.java)

package ch09.sec07;

public class Home {
	private RemoteControl rc = new RemoteControl() {
		@Override
		public void turnOn() {
			System.out.println("TV를 켭니다.");
		}

		@Override
		public void turnOff() {
			System.out.println("TV를 끕니다.");
		}
	};

	public void use1() {
		rc.turnOn();
		rc.turnOff();
	}

	public void use2() {
		RemoteControl rc = new RemoteControl() {
			@Override
			public void turnOn() {
				System.out.println("에어컨을 켭니다.");
			}

			@Override
			public void turnOff() {
				System.out.println("에어컨을 끕니다.");
			}
		};

		rc.turnOn();
		rc.turnOff();
	}

	public void use3(RemoteControl rc) {
		rc.turnOn();
		rc.turnOff();
	}
}

📌 실행 클래스 (HomeExample.java)

package ch09.sec07;

public class HomeExample {
	public static void main(String[] args) {
		Home home = new Home();

		home.use1(); // 필드에서 미리 정의된 익명 객체 사용
		home.use2(); // 메서드 내에서 정의된 익명 객체 사용

		home.use3(new RemoteControl() {
			@Override
			public void turnOn() {
				System.out.println("난방을 켭니다.");
			}

			@Override
			public void turnOff() {
				System.out.println("난방을 끕니다.");
			}
		});
	}
}

💻  실행 결과

TV를 켭니다.
TV를 끕니다.
에어컨을 켭니다.
에어컨을 끕니다.
난방을 켭니다.
난방을 끕니다.

💬 코드 설명

구분 익명 구현 객체 위치 기능 정의
use1() 필드 TV 제어
use2() 지역 변수 에어컨 제어
use3() 매개변수로 전달 난방기 제어

✔ 모두 RemoteControl 인터페이스를 익명으로 구현한 객체를 사용


💡 포인트 정리

  • new 인터페이스() { ... } 형태로 즉석에서 구현체 생성
  • 클래스를 따로 만들 필요 없이 일회성 객체 생성 가능
  • 필드, 지역 변수, 매개변수 어디든 사용 가능
  • 실무에서는 버튼 이벤트, 기기 제어, 콜백 처리 등에서 자주 사용

📌 정리하자면,
이번 강의는 현실적인 콜백 구조나 이벤트 처리 로직에 바로 사용할 수 있는 핵심 예제입니다.
인터페이스만 정의해두고, 필요할 때마다 익명 구현 객체로 원하는 동작을 손쉽게 정의할 수 있습니다.

댓글
반응형

이번 글에서는 익명 구현 객체(Anonymous Object) 를 사용하여
클래스의 메서드를 재정의하고 실행하는 방법을 학습합니다.
이름 없이 즉석에서 클래스를 선언하고 객체로 사용하는 방식으로,
주로 콜백 처리일회성 재정의에 활용됩니다.


📦 기본 클래스 정의 (Tire.java)

package ch09.sec07;

public class Tire {
	public void roll() {
		System.out.println("일반 타이어가 굴러갑니다.");
	}
}

📌 자동차 클래스 (Car.java)

package ch09.sec07;

public class Car {
	private Tire tire1 = new Tire();

	// 익명 자식 객체를 필드에 직접 대입
	private Tire tire2 = new Tire() {
		public void roll() {
			System.out.println("익명 자식 Tire 객체 1이 굴러갑니다.");
		}
	};

	public void run1() {
		tire1.roll(); // 일반 타이어
		tire2.roll(); // 익명 자식 객체
	}

	public void run2() {
		Tire tire = new Tire() {
			public void roll() {
				System.out.println("익명 자식 Tire 객체 2이 굴러갑니다.");
			}
		};
		tire.roll();
	}

	public void run3(Tire tire) {
		tire.roll();
	}
}

📌 실행 클래스 (CarExample.java)

package ch09.sec07;

public class CarExample {
	public static void main(String[] args) {
		Car car = new Car();

		car.run1();
		car.run2();

		car.run3(new Tire() {
			public void roll() {
				System.out.println("익명 자식 Tire 객체 3이 굴러갑니다.");
			}
		});
	}
}

💻 실행 결과

일반 타이어가 굴러갑니다.
익명 자식 Tire 객체 1이 굴러갑니다.
익명 자식 Tire 객체 2이 굴러갑니다.
익명 자식 Tire 객체 3이 굴러갑니다.

💬 코드 설명

  • new Tire() { ... }
    이름 없는 Tire 클래스의 자식 객체 생성
  • tire1: 일반 객체
  • tire2: 필드에 익명 객체 직접 대입
  • run2(): 지역 변수로 익명 객체 선언
  • run3(): 매개변수로 익명 객체 전달

✔ 즉, 필드, 지역변수, 매개변수 어디서든 익명 구현 객체 사용 가능


📌 익명 객체 특징 정리

위치 작성 형태 용도
필드 private Tire tire = new Tire(){} 1회용 자식 객체를 멤버로 사용
지역변수 Tire tire = new Tire(){} 메서드 내에서 일시적 객체로 사용
매개변수 method(new Tire(){}) 전달할 때 바로 구현체 지정

💡 포인트 정리

  • 익명 구현 객체는 클래스 이름이 없음
  • 즉석에서 클래스를 정의하면서 객체 생성
  • 메서드 오버라이딩 가능
  • 일반적으로 인터페이스 또는 추상 클래스 구현에 사용되지만
    일반 클래스 상속도 가능

📌 정리하자면,
익명 구현 객체는 코드를 짧고 간결하게 만들면서도,
일회성 동작을 유연하게 처리할 수 있는 강력한 도구입니다.
특히 리스너, 콜백, 전략 객체 등에서 많이 사용됩니다.

댓글
반응형
항목 익명 객체 람다식 내부 클래스
🌟 정의 이름 없는 클래스를 즉석에서 구현 함수형 인터페이스를 간단히 표현 클래스 안에 정의된 또 다른 클래스
🧱 형태 new 인터페이스() { ... } (매개변수) -> { 코드 } class Inner { ... }
🧠 목적 일회성 클래스 구현 (보통 인터페이스나 추상 클래스) 함수처럼 간단한 코드 전달 (자바 8 이상) 외부 클래스와 밀접한 관계가 있는 구조적 코드
📋 제한 생성자 사용 불가, 클래스 이름 없음 함수형 인터페이스만 가능 static 여부에 따라 인스턴스 필요 유무 다름
📦 코드 양 중간 (중괄호 + 오버라이드 필요) 아주 간결 (함수처럼 표현 가능) 길어질 수 있음 (정식 클래스니까요)
🎯 사용 예 Runnable, ActionListener, 콜백 등 Comparator, Stream 처리 등 UI 이벤트, 구조적 묶음 클래스
📚 버전 제한 자바 1.1부터 가능 자바 8 이상만 가능 자바 초창기부터 존재

✏️ 코드 예제 비교

✅ 익명 객체

Runnable r = new Runnable() {
    public void run() {
        System.out.println("익명 객체!");
    }
};
r.run();

✅ 람다식

Runnable r = () -> System.out.println("람다식!");
r.run();

✅ 내부 클래스

public class Outer {
    class Inner {
        void hello() {
            System.out.println("내부 클래스!");
        }
    }
}

💡 간단 요약

상황 추천 방식
한 번 쓰는 간단한 인터페이스 구현 ✅ 익명 객체
함수형처럼 표현하고 싶다 ✅ 람다식
클래스 내부에서 기능을 나눠 구조적으로 관리하고 싶다 ✅ 내부 클래스

즉, 람다식은 익명 객체의 축약 버전이라고 볼 수 있습니다.
내부 클래스는 구조적인 목적에 더 가깝습니다.
각각 장단점이 있어서, 상황에 맞게 고르는 것이 중요합니다.

😆 연관된 글

80. 인터페이스와 람다식 – 익명 객체 대신 사용하는 간결한 표현

81. 인스턴스 멤버 클래스 – 클래스 안에 클래스가 있다고요?

82. 정적(static) 멤버 클래스 – 외부 객체 없이도 작동하는 중첩 클래스

83. 정적 멤버 클래스의 독립성 – 외부 인스턴스 없이 자유롭게 활용

84. 정적 멤버 클래스의 활용 – 인스턴스와 static 멤버를 모두 가진 클래스

85. 로컬 클래스(Local Class) – 메서드 안에 클래스가?!

86. 인스턴스 멤버 클래스 vs 정적 멤버 클래스 – 접근 가능한 멤버는?

87. 내부 클래스에서 외부 클래스 참조하기 – this vs A.this

88. 이벤트 처리를 위한 로컬 클래스 구현 – 콜백 메커니즘 만들기

89. 익명 구현 객체 – 한 번만 쓰고 버리는 1회용 클래스 만들기

90. 인터페이스 기반 익명 구현 객체 – 직접 클래스 만들지 않아도 OK

91. 람다식으로 더 간결하게! 인터페이스 구현의 진화

댓글
반응형

이번 강의에서는 이벤트 처리 방식에서 자주 사용하는 콜백 인터페이스와 로컬 클래스 구조를 학습합니다.
인터페이스를 구현한 로컬 클래스를 생성하여
버튼 클릭 시 동작이 실행되는 구조를 이해하는 것이 핵심입니다.


📦 콜백 인터페이스 포함 클래스 (Button.java)

package ch09.sec06;

public class Button {
	public static interface ClickListener {
		void onClick();
	}

	private ClickListener clickListener;

	public void setClickListener(ClickListener clickListener) {
		this.clickListener = clickListener;
	}

	public void click() {
		this.clickListener.onClick();
	}
}

✔ ClickListener는 버튼 클릭 시 호출되는 콜백 인터페이스
✔ setClickListener() 메서드로 이벤트 리스너 등록
✔ click() 호출 시 리스너의 onClick() 실행


📌 실행 클래스 (ButtonExample.java)

package ch09.sec06;

public class ButtonExample {
	public static void main(String[] args) {
		Button btnOk = new Button();

		class OkListener implements Button.ClickListener {
			@Override
			public void onClick() {
				System.out.println("Ok 버튼을 클릭했습니다.");
			}
		}
		btnOk.setClickListener(new OkListener());
		btnOk.click();

		Button btnCancel = new Button();

		class CancelListener implements Button.ClickListener {
			@Override
			public void onClick() {
				System.out.println("Cancel 버튼을 클릭했습니다.");
			}
		}
		btnCancel.setClickListener(new CancelListener());
		btnCancel.click();
	}
}

💻 실행 결과

Ok 버튼을 클릭했습니다.
Cancel 버튼을 클릭했습니다.

💬 코드 설명

  • OkListener, CancelListener
    Button.ClickListener 인터페이스를 구현한 로컬 클래스(Local Class)
  • 각각 다른 버튼(btnOk, btnCancel)다른 동작을 연결하고 있음
  • btn.setClickListener(...) 를 통해
    버튼 객체에 클릭 시 동작할 리스너를 주입(inject)
  • click() 메서드 실행 시 해당 리스너의 onClick() 호출됨

📌 간단 정리

버튼 객체 등록된 리스너 클래스 클릭 시 동작 출력
btnOk OkListener "Ok 버튼을 클릭했습니다."
btnCancel CancelListener "Cancel 버튼을 클릭했습니다."

💡 포인트 정리

  • 로컬 클래스는 메서드 내에서 선언되고 해당 블록에서만 사용
  • 인터페이스를 구현한 익명/로컬 클래스를 이벤트 리스너로 자주 사용
  • 콜백 구조는 UI 이벤트 처리, 비동기 동작 처리에 매우 자주 활용
  • 인터페이스 + 구현 객체 주입 → 동작 분리와 유연한 구조 가능

📌 정리하자면,
이번 예제는 자바에서 인터페이스를 통한 이벤트 처리 구조의 기초를 보여주는 구조입니다.
로컬 클래스를 통해 일회성 동작을 간단하게 분리할 수 있고,
버튼마다 다른 동작을 지정할 수 있는 유연한 구조를 만들 수 있습니다.

댓글
반응형

이번 글에서는 자바의 인스턴스 멤버 클래스 내부에서 this와 외부 클래스 참조를 어떻게 구분하고 사용하는지를 배웁니다.
내부 클래스와 외부 클래스에 같은 이름의 필드나 메서드가 존재할 때,
어떤 것이 호출되는지 명확히 구분하는 방법을 학습합니다.


📦 외부 클래스 (A.java)

package ch09.sec05.exam02;

public class A {
	String field = "A-field";

	void method() {
		System.out.println("A-method");
	}

	class B {
		String field = "B-field";

		void method() {
			System.out.println("B-method");
		}

		void print() {
			System.out.println(this.field);       // 내부 클래스 B의 field
			this.method();                        // 내부 클래스 B의 method()
			System.out.println(A.this.field);     // 외부 클래스 A의 field
			A.this.method();                      // 외부 클래스 A의 method()
		}
	}

	void useB() {
		B b = new B();
		b.print();
	}
}

📌 실행 클래스 (AExample.java)

package ch09.sec05.exam02;

public class AExample {
	public static void main(String[] args) {
		A a = new A();
		a.useB();
	}
}

💻 실행 결과

B-field
B-method
A-field
A-method

💬 코드 설명

  • this.field, this.method()
    → 내부 클래스 B의 멤버를 참조
  • A.this.field, A.this.method()
    → 외부 클래스 A의 멤버를 참조

✔ 내부 클래스가 외부 클래스 인스턴스와 연결되어 있기 때문에
A.this를 통해 외부 클래스의 멤버에 접근 가능!


📌 간단 정리

참조 표현 실제 대상
this.field 클래스 B의 필드
A.this.field 클래스 A의 필드
this.method() 클래스 B의 메서드
A.this.method() 클래스 A의 메서드

💡 포인트 정리

  • 내부 클래스는 외부 클래스의 인스턴스를 포함한 객체처럼 동작
  • 내부 클래스와 외부 클래스에 동일한 이름의 필드/메서드가 있을 때는 this, A.this로 구분
  • 외부 클래스 이름을 명시한 A.this는 외부 인스턴스를 직접 참조하는 문법
  • 내부 클래스는 외부 인스턴스와 연결되어야 생성 가능

📌 정리하자면,
내부 클래스에서 외부 클래스와 이름이 같은 필드나 메서드가 충돌할 경우,
this와 A.this를 이용해 정확하게 참조할 수 있어야 합니다.
특히 복잡한 클래스 구조에서 매우 유용하게 쓰이는 개념입니다.

댓글
반응형

이번 글에서는 로컬 클래스(Local Class) 를 학습합니다.
로컬 클래스는 메서드나 생성자 내부에 선언되는 클래스로,
짧은 범위에서 한정적으로 필요한 클래스를 정의할 때 사용합니다.


📦 클래스 구조 (A.java)

package ch09.sec04.exam01;

public class A {
	A() {
		class B { }     // 생성자 내부 로컬 클래스
		B b = new B();  // 객체 생성
	}

	void method() {
		class B { }     // 메서드 내부 로컬 클래스
		B b = new B();  // 객체 생성
	}
}

📌 핵심 포인트

특징 설명
선언 위치 생성자, 메서드, 초기화 블록 등 내부에서만 선언 가능
접근 제한자 사용 불가 (public, private, static 등 사용 불가능)
사용 범위 선언된 블록 내에서만 사용 가능
외부 클래스 접근 외부 클래스의 멤버(필드, 메서드 등) 접근 가능 (final 또는 effectively final일 경우)

📌 코드 설명

  • class B는 생성자 또는 메서드 내부에 선언됨
  • 이 클래스는 선언된 블록 내부에서만 사용 가능
  • B b = new B();는 같은 블록 내에서 B의 객체를 생성

💻 실행 결과

// 실행 결과 없음 (정상 컴파일 및 객체 생성)

💬 예시 추가: 외부 멤버 접근

public class A {
	int value = 10;

	void method() {
		int local = 5;
		class B {
			void print() {
				System.out.println("value = " + value); // 외부 필드 접근 가능
				System.out.println("local = " + local); // 지역 변수 접근 (final 또는 effectively final)
			}
		}
		B b = new B();
		b.print();
	}
}

💡 포인트 정리

  • 로컬 클래스는 한정된 기능을 수행하는 용도로 좋음
  • 서드/생성자 내부에서 클래스가 필요한 경우 사용
  • 지역 변수에 접근 시에는 해당 변수가 final이거나 사실상 변경되지 않아야 함
  • 익명 구현 객체로 발전할 수 있는 기반 구조

📌 정리하자면,
로컬 클래스는 짧은 범위에서 클래스가 필요할 때 가장 간편한 방법입니다.
특정 메서드 안에서만 사용할 작은 기능이 있다면,
로컬 클래스를 활용해 코드를 깔끔하게 정리할 수 있습니다.

댓글