5주차 학습 진도 : Chapter 8, 9 (인터페이스, 중첩 클래스와 중첩 인터페이스)
Chapter 8 인터페이스
8-1 인터페이스
인터페이스 선언
인터페이스는 클래스가 구현해야 하는 메서드의 집합을 정의합니다. 인터페이스는 interface 키워드를 사용하여 선언합니다. 인터페이스 내의 메서드는 기본적으로 추상 메서드로 정의되며, 메서드의 몸체는 포함되지 않습니다. 아래는 간단한 인터페이스의 예입니다:
public interface RemoteControl {
// 상수
public int MAX_VOLUME = 10;
public int MIN_VOLUME = 0;
// 추상 메소드
public void turnOn();
public void turnOff();
public void setVolume(int volume);
}
인터페이스 구현
클래스는 인터페이스를 구현하기 위해 implements 키워드를 사용합니다. 한 클래스는 여러 개의 인터페이스를 구현할 수 있으며, 이때 각 인터페이스의 모든 메소드를 구현해야 합니다. 아래는 RemoteControl 인터페이스를 구현한 Television 클래스의 예입니다.
public class Television implements RemoteControl {
public void turnOn() {
System.out.println("TV를 켭니다.");
}
public void turnOff() {
System.out.println("TV를 끕니다.");
}
}
인터페이스 사용
인터페이스를 사용하면 객체지향 프로그래밍의 다형성을 활용할 수 있습니다. 인터페이스 타입으로 변수 선언 후, 이를 구현한 객체를 할당하여 메소드를 호출할 수 있습니다. 다음은 RemoteControl 인터페이스를 사용하는 간단한 예제입니다.
public class MyClass {
RemoteControl rc = new Television();
MyClass(RemoteControl rc) {
this.rc = rc;
rc.turnOn();
rc.setVolume(5);
}
}
public class MyClassExample {
public static void main(String[] args) {
MyClass myClass1 = new MyClass();
myClass1.rc.turnOn();
myClass1.rc.setVolume(5);
}
}
8-2 타입 변환과 다형성
자동 타입 변환
자바에서는 자식 클래스의 객체를 부모 클래스 타입으로 자동 변환할 수 있습니다. 이는 자식 클래스가 부모 클래스의 모든 특성을 가지기 때문입니다.
필드의 다형성
필드의 다형성은 객체지향 프로그래밍에서 중요한 개념으로, 동일한 타입의 필드가 다양한 구현체를 참조할 수 있게 해줍니다. 이를 통해 코드의 유연성과 확장성을 높일 수 있습니다.
public class Car {
Tire frontLeftTire = new HankookTire();
Tire frontRightTire = new HankookTire();
Tire backLeftTire = new HankookTire();
Tire backRightTire = new HankookTire();
void run() {
frontLeftTire.roll();
frontRightTire.roll();
backLeftTire.roll();
backRightTire.roll();
}
}
매개 변수의 다형성
메서드 매개변수로 인터페이스 타입을 사용하면 다양한 객체를 전달할 수 있습니다. 이는 코드의 유연성을 높이는 중요한 요소입니다.
public class Driver {
public void drive(Vehicle vehicle) {
vehicle.run();
}
}
강제 타입 변환
자동 타입 변환 후, 원래의 객체 타입으로 다시 변환할 수 있습니다. 이때는 명시적으로 타입을 지정해야 하며, 올바른 타입으로 변환하지 않으면 ClassCastException이 발생할 수 있습니다.
public class VehicleExample {
public static void main(String[] args) {
Vehicle vehicle = new Bus();
vehicle.run();
Bus bus = (Bus) vehicle; // 강제 타입 변환
bus.run();
bus.checkFare();
}
}
객체 타입 확인
instanceof 연산자를 사용하여 객체의 타입을 확인할 수 있습니다. 이를 통해 안전하게 강제 타입 변환을 수행할 수 있습니다.
public class Driver {
public void drive(Vehicle vehicle) {
if(vehicle instanceof Bus) {
Bus bus = (Bus) vehicle;
bus.checkFare();
}
}
}
인터페이스 상속
인터페이스는 다른 인터페이스를 상속받을 수 있습니다. 이는 인터페이스 간의 관계를 형성하고, 메서드의 집합을 작성하는데 유용합니다.
public interface InterfaceC extends interfaceA, interfaceB {
public void methodC();
}
Chapter 9. 중첩 클래스와 중첩 인터페이스
9-1 중첩 클래스와 중첩 인터페이스 소개
중첩 클래스
중첩 클래스는 다른 클래스의 내부에 정의된 클래스를 말합니다. 중첩 클래스는 외부 클래스의 멤버 변수와 메서드에 직접 접근할 수 있습니다.
class A {
class B {
B() {} // 생성자
int field1; // 인스턴스 필드
void method1() { //인스턴스 메서드
System.out.println("method1 호출됨");
}
}
public static void main(String[] args) {
A outer = new A(); // 외부 클래스 인스턴스 생성
B inner = outer.new B(); // 중첩 클래스 인스턴스 생성
inner.field1 = 10; // 필드 값 설정
inner.method1(); // 메서드 호출
System.out.println("field1: " + inner.field1); // 필드 값 출력
}
}
중첩 클래스의 접근 제한
중첩 클래스는 외부 클래스의 접근 제한자에 따라 접근할 수 있는 범위가 달라집니다. 예를 들어, private로 선언된 외부 클래스의 멤버는 내부 클래스에서 접근할 수 있습니다.
public class A {
private class B {
void display() {
System.out.println("B 클래스의 메서드");
}
}
public class C {
void display() {
System.out.println("C 클래스의 메서드");
}
}
void method1() {
B b = new B(); // B는 A 내에서만 접근 가능
C c = new C(); // C는 외부에서도 접근 가능
b.display();
c.display();
}
public static void main(String[] args) {
A a = new A();
a.method1(); // 메서드 호출
}
}
중첩 인터페이스
중첩 인터페이스는 클래스 내부에 정의된 인터페이스입니다. 중첩 인터페이스는 외부 클래스의 멤버로 사용할 수 있으며, 주로 외부 클래스와 관련된 특정 기능을 정의할 때 유용합니다.
class A {
[static] interface I { // 중첩 인터페이스
void method();
}
}
9-2 익명 객체
익명 객체는 이름이 없는 클래스의 객체로, 주로 일회성으로 사용됩니다. 익명 객체는 인터페이스를 구현하거나 추상 클래스를 상속받을 때 사용됩니다.
익명 자식 객체 생성
익명 자식 객체는 인터페이스를 구현하는 익명 객체를 생성할 때 사용됩니다.
class Parent {}
class A {
Parent field = new Parent(); // 익명 자식 객체 생성
void method() {
Parent localVar = new Parent(); // 로컬 변수에 익명 자식 객체를 대입
}
}
익명 구현 객체 생성
추상 클래스를 상속받는 익명 객체도 생성할 수 있습니다. 이를 통해 간편하게 특정 클래스의 기능을 구현할 수 있습니다.
class A {
void method1(RemoteControl rc) { }
void method2() {
method1( // method1() 메서드 호출
new RemoteControl() { // method1()의 매개값으로 익명 구현 객체를 대입
@Override
void turnOn() { }
}
);
}
}
익명 객체의 로컬 변수 사용
익명 객체 내에서 로컬 변수에 접근하려면 그 변수가 final이거나 사실상 final이어야 합니다. 이는 익명 클래스가 로컬 변수를 캡처할 수 있도록 보장하는 자바의 규칙입니다.
class Anonymous {
void method(int x, int y) {
System.out.println("x: " + x + ", y: " + y);
}
}
public class AnonymousExample {
public static void main(String[] args) {
// 익명 객체 생성
new Anonymous() {
@Override
public void method(final int x, int y) {
System.out.println("Anonymous - x: " + x + ", y: " + y);
}
}.method(0, 0); // 메서드 호출
}
}
기본 숙제(필수) : 클래스를 선언할 때 인터페이스는 어떻게 선언 될 수 있는지 정리하기
인터페이스는 interface키워드를 사용해 선언하며, 메서드는 기본적으로 추상적이고, 상수를 정의할 수 있습니다.
public interface RemoteControl {
// 상수
public int MAX_VOLUME = 10;
public int MIN_VOLUME = 0;
// 추상 메서드
public void turnOn();
public void turnOff();
public void setVolume(int volume);
}
클래스는 implements 키워드를 사용해 인터페이스를 구현하며, 모든 추상 메서드를 오버라이드해야 합니다.
public class Television implements RemoteControl {
// 필드
private int volume;
// turnOn() 추상 메서드의 실체 메서드
public void turnOn() {
System.out.println("TV를 켭니다.");
}
// turnOff() 추상 메서드의 실체 메서드
public void turnOff() {
System.out.println("TV를 끕니다.");
}
}
추가 숙제(선택) : p.443 (09-1) 확인 문제 3번 풀어보기
3. 다음과 같이 Car 클래스 냅부에 Tire와 Engine이 멤버 클래스로 선언되어 있습니다. 바깥클래스(NestedClassExample)에서 멤버 클래스의 객체를 생성하는 코드를 빈칸에 작성해 보세요.
package sec01.verify.exam03;
public class Car {
class Tire { }
static class Engine { }
}
package sec01.verify.exam03;
public class NestedClassExample {
public static void main(String[] args) {
Car myCar = new Car();
Car.Tire tire = myCar.new Tire();
Car.Engine engine = new Car.Engine();
}
}