[혼공자] 5주차_인터페이스

jini·2025년 8월 5일
0

혼자공부하는자바

목록 보기
5/6

한빛미디어의 <혼자 공부하는 자바>를 요약 정리했습니다.

 

🧊 5주차 미션

기본: 클래스를 선언할 때 인터페이스는 어떻게 선언될 수 있는지 정리하기

-> 인터페이스 > 인터페이스 사용에 정리

 

선택: p.443(09-1) 확인 문제 3번 풀어보기

다음과 같이 Car 클래스 내부에 Tire와 Engine이 멤버 클래스로 선언되어 있습니다. 바깥 클래스(NestedClassExample)에서 멤버 클래스의 객체를 생성하는 코드를 빈칸에 작성해 보세요.

Car.java

public class Car {
	class Tire { }
    static class Engin { }
}

NestedClassExample.java

public class NestedClassExample {
	public static void main(String[] args) {
    	Car myCar = new Car();
        
        // 인스턴스 멤버 클래스는 바깥 객체를 통해 생성
        Car.Tire tire = myCar.new Tire();

        // static 멤버 클래스는 바깥 객체 없이 생성 가능
        Car.Engine engine = new Car.Engine();
    }
}
  • 인스턴스 멤버 클래스 → 바깥객체.new 내부클래스()
  • static 멤버 클래스 → new 바깥클래스.내부클래스()

 

🧊 인터페이스

개발 코드와 객체가 서로 통신하는 접점 역할

  • 코드가 인터페이스의 메소드를 호출하면 인터페이스는 객체의 메소드를 호출
  • 개발 코드는 객체의 내부 구조를 알 필요가 없음
  • 코드를 수정하지 않고도 사용하는 객체를 변경 가능

 

[1] 인터페이스 선언

[public] interface 인터페이스이름 {
	// 상수
    [public static final] 타입 상수이름 =;
    public int MAX_VOLUME = 10;
    
    // 추상 메소드
    [public abstract] 리턴타입 메소드이름(매개변수, ...);
    public void setColume(int volume);
}
  • 영어 대소문자를 구분하며, 첫 글자를 대문자로 하고 나머지는 소문자로 작성
  • 상수 필드와 추상 메소드만을 구성 멤버로 가짐
  • 객체로 생성할 수 없기 때문에 생성자를 가질 수 없음

 

상수 필드 선언(constant field)
객체 사용 방법을 정의한 것이므로 실행 시 데이터를 저장할 수 있는 인스턴스 또는 정적 필드 선언 불가능

  • 선언된 필드는 모두 public static final의 특성을 가짐(생략 가능)
  • 대문자로 작성하고, 서로 다른 단어로 구성되어 있을 경우 언더바로 연결

 

추상 메소드 선언(abstract method)
인터페이스를 통해 호출된 메소드는 객체에서 실행되므로 실행 블록이 필요없는 추상 메소드로 선언

  • 리턴 타입, 메소드 이름, 매개 변수만 기술(중괄호를 붙이지 않음)
  • 선언된 추상 메소드는 모두 public abstract의 특성을 가짐(생략 가능)

 

IntelliJ에서 인터페이스 생성

  • 인터페이스를 포함할 패키지를 생성
  • 패키지 우클릭 > New > Java Class > Interface
  • RemoteControl 인터페이스의 선언부를 자동적으로 만들어줌

 

[2] 인터페이스 구현

객체는 인터페이스에서 정의된 추상 메소드와 동일한 메소드 이름, 매개 타입, 리턴 타입을 가진 실체 메소드를 가지고 있어야 함
-> 인터페이스의 구현(implement) 객체, 구현 객체를 생성하는 클래스를 구현 클래스

 

구현 클래스

public class 구현클래스이름 implements 인터페이스이름 {
	// 인터페이스에 선언된 추상 메소드의 실체 메소드 선언
}

// example
public class Television implements RemoteControl {
	// 필드
    private int volume;
    
    // setVolume() 추상 메소드의 실체 메소드
    public void setVolume(int volume) {
    	if (volume > RemoteControl.MAX_VOLUME) {
        	this.volume = RemoteControl.MAX_VOLUME;
        } else if (volume < RemoteControl.MIN_VOLUME) {
     
        ....
    }

구현 클래스가 작성되면 new 연산자로 객체 생성 가능

  • 인터페이스로 구현 객체를 사용하려면 인터페이스 변수를 선언하고 구현 객체를 대입
  • 인터페이스 변수는 참조타입이므로 구현 객체가 대입될 경우 구현 객체의 번지를 저장
public class RemoteControlExample {
	public static void main(String[] args) {
    	RemoteControl rc;
        rc = new Television();
        rc = new Television();
    }
}

 

다중 인터페이스 구현 클래스
객체는 다수의 인터페이스 타입으로 사용 가능

  • 인터페이스 A, B가 객체의 메소드를 호출할 수 있으려면 객체가 두 인터페이스를 모두 구현
public class 구현클래스이름 implements 인터페이스A, 인터페이스B {
	// 인터페이스 A에 선언된 추상 메소드의 실체 메소드 선언
    // 인터페이스 B에 선언된 추상 메소드의 실체 메소드 선언
}

 

[3] 인터페이스 사용

클래스를 선언할 때 인터페이스는 필드, 생성자 또는 메소드의 매개 변수, 생성자 또는 메소드의 로컬 변수로 선언될 수 있음

 

example: Remote Control 인터페이스를 필드, 생성자 또는 메소드의 매개 변수 그리고 메소드의 로컬 변수로 선언

public class MyClass {
	// 필드
    RemoteControl rc = new Television();
    
    // 생성자
    MyClass(RemoteControl rc) {  // 생성자의 매개값으로 구현 객체 대입
    	this.rc = rc;
    }
    
    // 메소드
    void methodA() {
    	// 로컬 변수
        RemoteControl rc = new Audio();
    }
    
    void methodB(RemoteCointrol rc) { ... }  // 생성자의 매개값으로 구현 객체 대입
}
  • 인터페이스가 필드 타입으로 사용될 경우, 필드에 구현 객체를 대입 가능
  • 인터페이스가 생성자의 매개 변수 타입으로 사용될 경우, new 연산자로 객체를 생성할 때 구현 객체를 생성자의 매개값으로 대입 가능
  • 인터페이스가 로컬 변수 타입으로 사용될 경우, 변수에 구현 객체를 대입 가능
  • 인터페이스가 메소드의 매개 변수 타입으로 사용될 경우, 메소드 호출 시 구현 객체를 매개값으로 대입 가능

 

  1. 필드로 선언된 rc는 다음과 같이 사용 가능
MyClass myClass = new MyClass();
myClass.rc.turnOn();  // Television의 turnOn()이 실행
myClass.rc.setVolume(5);  // Television의 setVolume(5)가 실행

 

  1. 생성자의 매개 변수 타입으로 선언된 rc는 다음과 같이 사용 가능
  • 다음과 같이 MyClass 객체가 생성되었을 경우, Audio의 turnOn()과 setVolune(5)가 실행
MyClass( RemoteControl rc ) {
	this.rc = rc;
    rc.turnOn();
    rc.setVolume(5);
}

Myclass myclass = new MyClass(new Audio());

 

  1. 로컬 변수로 선언된 rc는 다음과 같이 사용 가능
void methodA() {
    RemoteControl rc = new Audio();
    rc.turnOn();  // Audio의 turnOn()이 실행
    rc.setVolume(5);  // Audio의 setVolume(5)가 실행
}

 

  1. 메소드의 매개 변수 타입으로 선언된 rc는 다음과 같이 사용 가능
  • methodB() 메소드가 호출되었을 경우, Television의 turnOn()과 setVolune(5)가 실행
void methodB() {
    rc.turnOn(); 
    rc.setVolume(5);  
}

Myclass myclass = new MyClass();
myClass.methodB(new Television());

 

🧊 타입 변환과 다형성

인터페이스를 사용해서 메소드를 호출하도록 구현했다면, 구현 객체를 빠르게 고체 가능
-> 인터페이스의 다형성

// A에서 B로 수정
I i = new A();
I i = new B();  

// 수정 필요 없음
i.method1();
i.method2();

 

[1] 자동 타입 변환(promotion)

프로그램 실행 도중에 자동적으로 타입 변환이 일어나는 것

  • 구현 객체가 인터페이스 타입으로 변환되는 것
인터페이스 변수 = 구현객체;
  • 인터페이스 구현 클래스를 상속해서 자식 클래스를 만들었다면, 자식 객체도 인터페이스 타입으로 자동 타입 변환 가능

 

[2] 필드의 다형성

한국 타이어와 금호 타이어는 공통적으로 타이어 인터페이스를 구현했기 때문에 모두 타이어 인터페이스에 있는 메소드를 가지고 있음
Tire.java

public interface Tire {
	public void roll();
}

HankookTire.java

puclic class HankookTire implements Tire {
	@Override
    public void roll() {
    	System.out.println("한국 타이어가 굴러갑니다.");
    }
}

KumhoTire.java

public class KumhoTire implememts Tire {
	@Override
    public void roll() {
    	System.out.println("금호 타이어가 굴러갑니다.");
	}
}

Car.java

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();
	}
}

CarExample.java

public class CarExample {
	public static void main(String[] args) {
    	Car myCar = new Car();
        
        myCar.run();
        
        myCar.frontLeftTire = new KumhoTire();
        myCar.frontRightTire = new KumhoTire();
        
        myCar.run();
	}
}

 

[3] 매개 변수의 다형성

자동 타입 변환은 필드의 값을 대입할 때에도 발생하지만, 주로 메소드를 호출할 때 발생

example: 매개 변수를 인터페이스 타입으로 선언하고 호출할 때에는 구현 객체를 대입

public class Driver {
	public void drive(Vehicle vehicle) {  // 구현 객체
    	vehicle.run();  // 구현 객체의 run() 메소드가 실행됨
    }
}

public interface Vehicle {
	public void run();
}

public class Bus implements Vehicle {
	@Override
    public void run() {
    	System.out.println("버스가 달립니다.");
	}
}

Bus가 구현 클래스라면 Driver의 drive() 메소드르 호출할 때 Bus 객체를 생성해서 매개값으로 줌

Vehicle vehicle = bus  // 자동 타입 변환
  • drive() 메소드는 Vehicle 타입을 매개 변수로 선언했지만, Vehicle을 구현한 Bus 객체가 매개값으로 사용되면 자동 타입 변환이 발생

-> 매개 변수의 타입이 인터페이스일 경우 어떠한 구현 객체도 매개값으로 사용할 수 있음

 

[4] 강제 타입 변환(casting)

구현 객체가 인터페이스 타입으로 자동 타입 변환하면, 이터페이스에 선언된 메소드만 사용 가능하다는 제약

  • 강제 타입 변환을 해서 다시 구현 클래스 타입으로 변환하면, 구현 클래스의 필드와 메소드 사용 가능
구현클래스 변수 = (구현클래스) 인터페이스변수;

// example
interface Vehicle {
	void run();
}

class Bus implements Vehicle {
	void run() { ... }
    void checkFare() { ... };
}

Vehicle vehicle = new Bus();

vehicle.run();  // 가능
vehicle.checkFare();  // 불가능

Bus bus = (Bus) vehicle;  // 강제 타입 변환

bus.run();  // 가능
bus.checkFare();  // 가능

 

[5] 객체 타입 확인

강제 타입 변환은 구현 객체가 인터페이스 타입으로 변환되어 있는 상태에서 가능

  • 어떤 구현 객체가 변환되어 있는지 알 수 없는 상태에서 강제 타입 변환할 경우 ClassCastExveption 발생
if (vehicle instanceof Bus) {
	Bus bus = (Bus) vehicle;
}

// example
public class Driver {
	public void drive(Vehicle vehicle) {
    	if(vehicle instanceof Bus) {  // vehicle 매개 변수가 참조하는 객체가 Bus인지 조사
        	Bus bus = (Bus) vehicle;  // Bus 객체일 경우 안전하게 강제 타입 변환
            bus.checkFare();  // Bus 타입으로 강제 타입 변환을 하는 이유
        }
        vehicle.run();

 

[6] 인터페이스 상속

인터페이스도 다른 인터페이스를 상속 가능

  • 클래스와 달리 다중 상속을 허용
public interface 하위인터페이스 extends 상위인터페이스1, 상위인터페이스2 { ... }

하위 인터페이스를 구현하는 클래스는 하위 인터페이스의 메소드뿐만 아니라 상위 인터페이스의 모든 추상 메소드에 대한 실체 메소드를 가지고 있어야 함

하위인터페이스 변수 = new 구현클래스(...);
상위인터페이스1 변수 = new 구현클래스(...);
상위인터페이스2 변수 = new 구현클래스(...);
  • 하위 인터페이스로 타입 변환이 되면 상위 및 하위 인터페이스에 선언된 모든 메소드를 사용 가능
  • 상위 인터페이스로 타입 변환되면 상위 인터페이스에 선언된 메소드만 사용 가능하고, 하위 인터페이스에 선언된 메소드는 사용 불가능
public interface InterfaceA {
	public void methodA();
}

public interface InterfaceB {
	public void methodB();
}

public interface InterfaceC extends InterfaceA, InterfaceB {
	public void methodC();
}

public class ImplementionC implements InterfaceC {
	public void methodA() {
    	System.out.println("ImplementationC-methodA() 실행");
    }
    
    public void methodB() {
    	System.out.println("ImplementationC-methodB() 실행");
    }
    
    public void methodC() {
    	System.out.println("ImplementationC-methodA() 실행");
    }
}

public class Example {
	public static void main(Stirng[] args) {
    	Implementation impl = new ImplementationC();
        
        InterfaceA ia = impl;  // InterfaceA 변수는 methodA()만 호출 가능
        ia.methodA();

		Interface ib = impl;  // InterfaceB 변수는 methodB()만 호출 가능
        ib.methodB();
        
        Interface ic = impl;  // InterfaceC 변수는 methodA(), methodB(), methodC() 모두 호출 가능
        ic.methodA();
        ic.methodB();
        ic.methodC();
    }
}

 

🧊 중첩 클래스와 중첩 인터페이스

[1] 중첩 클래스(nested class)

클래스 내부에 선언한 클래스

  • 두 클래스의 멤버들을 서로 쉽게 접근할 수 있고, 외부에는 불필요한 관계 클래스를 감춤
  • 선언되는 위치에 따라서 두 가지로 분류
  • 컴파일하면 바이트 코드 파일(.class)가 별도로 생성
    • 멤버 클래스: A $ B .class
    • 로컬 클래스: A $1 B .class
class ClassName {
	class NestedClassName {
    }
}

 

멤버 클래스
클래스의 멤버로서 선언되는 중첩 클래스
클래스나 객체가 사용 중이라면 언제든지 재사용 가능

  • 인스턴스 멤버 클래스
    • A 객체를 생성해야만 사용할 수 있는 B 클래스
class A {
	class B { ... }
}
  • 정적 멤버 클래스
    • A 클래스로 바로 접근할 수 있는 B 클래스
class  A {
	static class B { ... }
}

 

로컬 클래스
생성자 또는 메소드 내부에서 선언되는 중첩 클래스
메소드를 실행할 때만 사용되고 메소드가 종료되면 없어짐

  • method()가 실행할 대만 사용할 수 있는 B 클래스
class A {
	void method() {
    	class B { ... }
    }
}

 

인스턴스 멤버 클래스
static 키워드 없이 중첩 선언된 클래스

  • 인스턴스 필드와 메소드만 선언이 가능하고 정적 필드와 메소드는 선언 불가능
class A {
	// 인스턴스 멤버 클래스
    class B {
    	B() { }  // 생성자
        int field1;  // 인스턴스 필드
        // static int field2;  // 정적 필드(x)
        void method() { }  // 인스턴스 메소드
        // static void method2() { }  // 정적 메소드(x)
	}
}
  • A 클래스 외부에서 B 객체를 생성하려면 먼저 A 객체를 생성하고 B 객체를 생성해야 함
A a = new A();
A.B b = a.new B();
b.field1 = 3;
b.method1();
  • A 클래스 내부의 생성자 및 인스턴스 메소드에서는 일반 클래스처럼 B 객체 생성 가능
class A {
	class B { .. }
    
    void methodA() {
    	B b = new B();
        b.field1 = 3;
        b.method1();
	}
}

 

정적 멤버 클래스
static 키워드로 선언된 클래스(모든 종류의 필드와 메소드 선언 가능)

class A {
	// 정적 멤버 클래스
    static class C {
    	C() { }  // 생성자
        int field1;  // 인스턴스 필드     
        static int field2;  // 정적 필드
        void method1() { }  // 인스턴스 메소드
        static void method2() { }  // 정적 메소드	

A 클래스 외부에서 정적 멤버 클래스 C를 생성하기 위해서는 다음과 같이 선언

A.C c = new A.C();
c.field1 = 3;  // 인스턴스 필드 사용
c.method1();  // 인스턴스 메소드 호출
A.C.field2 = 3;  // 정적 필드 사용
A.C.method2();  // 정적 메소드 호출

 

로컬 클래스
중첩 클래스는 메소드 내에서도 선언 가능 = 로컬 클래스

  • 접근 제한자(public, private) 및 static을 붙일 수 없음
  • 인스턴스 필드와 메소드만 선언 가능(정적 필드/메소드 불가)
void method() {
	// 로컬 클래스
    class D {
    	D() { }  // 생성자
        int field1;  // 인스턴스 필드
        // static int field2; 정적 필드 (x)
        void method1() { }; 인스턴스 메소드
        // static void method2{ }; 정적 메소드 (x)
	}
    D.d = new D();
    d.field1 = 3;
    d.method1();

 

[2] 중첩 클래스의 접근 제한

바깥 필드와 메소드에서 사용 제한
바깥 클래스에서 인스턴스 멤버 클래스를 사용할 때 제한이 있음

  • 인스턴스 멤버 클래스(B)는 바깥 클래스의 인스턴스 필드의 초기값이나 인스턴스 메소드에서 객체 생성 가능하지만, 정적 필드의 초기값이나 정적 메소드에서는 객체 생성 불가능
  • 정적 멤버 클래스(C)는 모든 필드의 초기값이나 메소드에서 객체 생성 가능
public class A {
	// 인스턴스 필드
    B field1 = new B();
    C field2 = new C();
    
    // 인스턴스 메소드
    B var1 = new B();
    C var2 = new C();
    
    // 정적 필드 초기화
    // static B field3 = new B();
    static C field4 = new C();
    
    // 정적 메소드
    static void method2() {
    	// B var1 = new B(); (x)
        C car2 = new C();
    
    // 인스턴스 멤버 클래스
    class B { }
    
    // 정적 멤버 클래스
	static class C { }
}

 

멤버 클래스에서 사용 제한
멤버 클래스가 인스턴스 또는 정적으로 선언됨에 따라 멤버 클래스 내부에서 바깥 클래스의 필드와 메소드에 접근할 때에도 제한

  • 인스턴스 멤버 클래스(B) 안에서는 바깥 클래스의 모든 필드와 메소드에 접근 가능
    정적 멤버 클래스(C) 안에서는 바깥 클래스의 정적 필드와 메소드에만 접근 가능(인스턴스 필드와 메소드는 접근 불가능)
class A {
	int field1;
    void method() { ... }
    
    static int field2;
    static void method2() { ... }
    
    class B {
    	void method() {
        	field1 = 10;
            method1();
            
            field2 = 10;
            method2();
	}
    
    static class C {
    	void method() {
        	field1 = 10;  // (x)
            method1();  // (x)
            
            field2 = 10;
            method2();
	}
}

 

로컬 클래스에서 사용 제한
메소드의 매개 변수나 로컬 변수를 로컬 클래스에서 사용할 때 제한

  • 로컬 클래스의 객체는 메소드 실행이 종료되면 없어지는 것이 일반적이지만, 메소드가 종류되어도 계속 실행 상태로 존재할 수 있음
    • ex. 로컬 스레드 객체를 사용할 때

컴파일 시 로컬 클래스에서 사용하는 매개 변수나 로컬 변수의 값을 로컬 클래스 내부에 복사해두고 사용

  • 매개 변수나 로컬 변수가 수정되어 값이 변경되면 로컬 클래스에 복사해둔 값과 달라지므로 매개 변수나 로컬 변수를 final로 선언할 것을 요구
  • 자바 8부터는 매개 변수나 로컬 변수를 final 선언을 하지 않아도 수정될 수 없도록 final 특성을 부여
public class Outter {
	public void method1([final] int arg) {
    	[final] int localVariable = 1;
		arg = 100;  // (x)
        localVariable = 100;  // (x)
        class Inner { 
        	public void method() {
            	int result = arg + localVariable;
            }
        }
}

 

중첩 클래스에서 바깥 클래스 참조 얻기
클래스 내부에서 this는 객체 자신의 참조

바깥클래스.this.필드
바깥클래스.this.메소드();

// example
public class Outter {
	String field = "Outter-field";
    void method() {
    	System.out.println("Outter-method");
    }
    
    class Nested {
    	String field = "Nested-field";
    void method() {
    	System.out.println("Nested-method");
    }
    }
    
    void print() {
    	System.out.println(this.field);  // Nested-field
        this.method();  // Nested-method
        System.out.println(Outter.this.field);  // Outter-field
        Outter.this.method();  // // Outter-method

 

[3] 중첩 인터페이스(nested interface)

클래스 내부에 선언한 인터페이스

  • 해당 클래스와 긴밀한 관계를 맺는 구현 클래스를 만듦
  • 인스턴스 멤버 인터페이스와 정적 멤버 인터페이스 모두 가능
    • 인스턴스 멤버 인터페이스는 바깥 클래스의 객체가 있어야 접근 가능
    • 정적 멤버 인터페이스는 바깥 클래스의 객체 없이 바깥 클래스 만으로 접근 가능
class A {
	[static] interface I {
    	void method();
	}
}

// example
public class Button {
	OnClickListener listener;
    
    void setOnClickListener(OnClickListener listener) {
    	this.listener = listener;
	}
    
    void touch() {
    	listener.onClick();
    }
    
    static interface OnClickListener {
    	void onClick();
    }
}

public class CallListener implements Button.OnClickListener {
	@Override
    public void onClick() {
    	System.out.println("전화를 겁니다.");
    }
}

public class ButtonExample {
	public static void main(String[] args) {
    	Button btn = new Button();
        
        btn.setOnClickListener(new CallListener());
        btn.touch();
    }
}

 

🧊 익명 객체

익명(anonymous) 객체는 이름이 없는 객체

  • 어떤 클래스를 상속하거나 인터페이스를 구현해야만 함

 

일반적인 경우에는 다음과 같이 명시적으로 클래스 이름을 주고 선언

[상속]
class 클래스이름1 extends 부모클래스 { ... }
부모클래스 변수 = new 클래스이름1();

[구현]
class 클래스이름2 implements 인터페이스 { ... }
인터페이스 변수 = new 클래스이름2();

 

익명 객체 생성

[상속]
부모클래스 변수 = new 부모클래스() { ... };

[구현]
인터페이스 변수 = new 인터페이스() { ... };

 

[1] 익명 자식 객체 생성

자식 클래스를 명시적으로 선언

  • 어디서건 이미 선언된 자식 클래스로 간단히 객체를 생성해서 사용 가능
  • 재사용성이 높음

재사용되지 않고, 오로지 특정 위치에서 사용할 경우 자식 클래스를 명시적으로 선언하지 않아도 됨

부모클래스 [필드|변수] = new 부모클래스(매개값, ...) {
	// 필드
    // 메소드
};
  • 중괄호 내부에는 필드나 메소드를 선언하거나 부모 클래스의 메소드를 재정의 하는 내용 작성

 

필드를 선언할 때 초기값으로 익명 자식 객체를 생성해서 대입하는 예

class A {
	Parent field = new Parent() {  // A 클래스의 필드 선언
    	int childField;
        void childMethod() { }
        @Override  // parent의 메소드를 재정의
        void parentMethod() { }
    };
}

 

메소드 내에서 로컬 변수를 선언할 때 초기값으로 익명 자식 객체를 생성해서 대입하는 예

class A {
	Parent localVar = new Parent() {  // 로컬 변수 선언
	    int childField;
        void childMethod() { }
        @Override  // Parent의 메소드를 재정의
        void parentMethod() { }
    };
}

 

메소드의 매개 변수가 부모 타입일 경우 메소드를 호출하는 코드에서 익명 자식 객체를 생성해서 매개값으로 대입하는 예

class A {
	void method1(Parent parent) { }
    
    void method2() {
    	method1(  // method1 메소드 호출
        	new Parent() {  // method1의 매개값으로 익명 자식 객체를 대입
            	int childField;
                void childMethod() { }
                @Override 
                void parentMethod() { }
            }
        );
    }
}

 

[2] 익명 구현 객체 생성

인터페이스 타입의 필드 또는 변수를 선언하고, 구현 객체를 초기값으로 대입하는 경우

// 일반적인 방법
class TV implements RemoteControl { }

class A {
	RemoteControl field = new TV();  // 필드에 구현 객체를 대입
    void method() {
    	RemoteControl localVar = new TV();  // 로컬 변수에 구현 객체를 대입
    }
}

// 익명 구현 객체 생성
인터페이스 [필드|변수] = new 인터페이스() {
	// 인터페이스에 선언된 추상 메소드의 실체 메소드 선언
    // 필드
    // 메소드
}

 

필드를 선언할 때 초기값으로 익명 구현 객체를 생성해서 대입하는 예

class A {
	RemoteControl field = new RemoteControl() {  // 클래스 A의 필드 선언
    	@Override  // RemoteControl 인터페이스의 추상 메소드에 대한 실체 메소드
    	void turnOn() { }
	};
}

 

메소드 내에서 로컬 변수를 선언할 때 초기값으로 익명 구현 객체를 생성해서 대입하는 예

void mtehod() {
	RemoteControl localVar = new RemoteControl() {  // 로컬 변수 선언
    	@Override  // RemoteControl 인터페이스의 추상 메소드에 대한 실체 메소드
    	void turnOn() { }
	};
}

 

메소드의 매개변수가 인터페이스 타입일 경우 메소드를 호출하는 코드에서 익명 구현 객체를 생성해서 매개값으로 대입하는 예

class A {
	void method1(ReoteControl rc) { }
    
    void mtehod2() {
    	method1(  // method1() 메소드 호출
        	new RemoteControl() {  // method1()의 매개값으로 익명 구현 객체를 대입
              @Override 
              void turnOn() { }
        };
    }
}

 

[3] 익명 객체의 로컬 변수 사용

메소드의 매개 변수나 로컬 변수를 익명 객체 내부에서 사용할 때도 제한

  • 익명 객체는 메소드 실행이 종료되면 없어지는 것이 일반적이지만, 메소드가 종료되어도 계속 실행 상태로 존재할 수 있음
    • ex. 익명 스레드 객체를 사용할 때 (메소드를 실행하는 스레드와 다르므로 메소드가 종료된 후에도 익명 스레드 객체는 실행 상태로 존재 가능)
  • 메소드의 매개 변수나 로컬 변수를 익명 객체 내부에서 사용할 때, 메소드 실해잉 끝나면 스택 메모리에서 사라짐
    -> 컴파일 시 익명 객체에서 사용하는 매개 변수나 로컬 변수의 값을 익명 객체 내부에 복사해두고 사용(매개 변수나 로컬 변수를 final로 선언)
public interface Calculatable {
	public int sum();
}

public class Anonymous { 
	private int field;
    
    public void method(final int arg1, int arg2) {
    	final int var1 = 0;
        int var2 = 0;
        
        arg1 = 20;  // (x)
        arg2 = 20;  // (x)
        
        var1 = 30;  // (x)
        var2 = 30;  // (x)
	}
}

0개의 댓글