728x90
반응형
인간의 욕심은 끝이없고 같은 실수를 반복한다...
또 날림.. 나는 자바랑 안 맞는 것일까....?
Chap 8 인터페이스
1. 인터페이스
- 인터페이스(interface) 개발 코드와 객체가 서로 통신하는 접점 역할을 한다.
- 개발 코드가 인터페이스의 메소드를 호출하면 인터페이스는 객체의 메소드를 호출시킴.
- 따라서 개발 코드는 객체의 내부 구조를 알 필요가 없고 인터페이스의 메소드만 알고 있으면 됨.
- 개발 코드가 직접 객체의 메소드를 호출하지 않는 이유?
- 개발 코드르 수정하지 않고 사용하는 객체를 변경할 수 있도록 하기 위해서
- 인터페이스는 하나의 객체가 아니라 여러 객체들과 사용이 가능하므로
어떤 객체를 사용하느냐에 따라서 실행 내용과 리턴값이 다를 수 있다 - 따라서 개발 코드 측면에서는 코드 변경 없이 실행 내용과 리턴값을 다양화할 수 있다는 장점이 있다
- 인터페이스는 .java 형태의 소스 파일로 작성되고 컴파일러(javac)를 통해 .class 형태로 컴파일되기 때문에
물리적 형태는 클래스와 동일하다 - 그러나 소스를 작성할 때 선언하는 방법이 다름
- 인터페이스 선언은 class 키워드 대신 interface 키워드 사용
- public 접근 제한은 다른 패키지에서도 인터페이스를 사용할 수 있도록 해줌
- 인터페이스는 객체로 생성할 수 없다. 즉, 생성자를 가질 수 없음
[이클립스에서 인터페이스 생성]
- Package Explorer 뷰에서 인터페이스를 포함할 패키지를 생성한 후 선택하고,
[File] - [New] - [Interface] 메뉴를 선택 - [New Java Interface] 대화상자가 나타나면 [Name] 입력란에 'RemoteControl'을 입력한 후 [Finish] 버튼을 클릭
- RemoteControl 인터페이스의 선언부를 자동으로 만들어 줌
- 클래스의 구성
- 필드, 생성자, 메소드
- 인터페이스
- 상수 필드, 추상 메소드
(1) 상수 필드 선언
- 인터페이스 객체 사용 방법을 정의한 것이므로
실행 시 데이터를 저장할 수 있는 인스턴스 또는 정적 필드를 선언할 수 없음 - 상수 필드(constant field)는 선언이 가능
- 상수는 인터페이스에 고정된 값으로 실행 시에 데이터를 바꿀 수 없다
상수 필드 선언 RemoteControl.java |
package sec01.exam02;
public interface RemoteControl { int MAX_VOLUME = 10; int MIN_VOLUME = 0; } |
(2) 추상 메소드 선언
- 인터페이스를 통해 호출된 메소드는 최종적으로 객체에서 실행
- 추상 메소드는 리턴 타입, 메소드 이름, 매개 변수만 기술되고 중괄호 {}를 붙이지 않는 메소드를 말함.
- 인터페이스에 선언된 추상 메소드는 모두 public abstract의 특성을 갖기 때문에
public abstract를 생략하더라도 컴파일 과정에서 자동으로 붙게 됨
추상 메소드 선언 // RemoteControl.java |
package sec01.exam03;
public interface RemoteControl { //상수 int MAX_VOLUME = 10; int MIN_VOLUME = 0; //추상 메소드 void turnOn(); void turnOff(); void setVolume(int volume); } |
2) 인터페이스 구현
- 개발 코드가 인터페이스 메소드를 호출하면 인터페이스는 객체의 메소드를 호출
- 객체는 인터페이스에서 정의된 추상 메소드와 동일한 메소드 이름, 매개 타입, 리턴 타입을 가진 실체 메소드를 가지고 있어야 함
- 이러한 객체를 인터페이스의 구현 객체라고 하고, 구현 객체를 생성하는 클래스를 구현 클래스라고 함.
(1) 구현 클래스
구현 클래스는 보통의 클래스와 동일한데,
인터페이스 타입으로 사용할 수 있음을 알려주기 위해
클래스 선언부에 implements 키워드를 추가하고 인터페이스 이름을 명시함
구현 클래스 // Television.java |
package sec01.exam04;
public class Television implements RemoteControl { //필드 private int volume; //turnOn() 추상 메소드의 실체 메소드 public void turnOn() { System.out.println("TV를 켭니다."); } //turnOff() 추상 메소드의 실체 메소드 public void turnOff() { System.out.println("TV를 끕니다."); } //setVolume() 추상 메소드의 실체 메소드 public void setVolume(int volume) { if(volume>RemoteControl.MAX_VOLUME) { this.volume = RemoteControl.MAX_VOLUME; } else if(volume<RemoteControl.MIN_VOLUME) { this.volume = RemoteControl.MIN_VOLUME; } else { this.volume = volume; } System.out.println("현재 TV 볼륨: " + this.volume); } } |
구현 클래스 // Audio.java |
package sec01.exam04;
public class Audio implements RemoteControl { //필드 private int volume; //turnOn() 추상 메소드의 실체 메소드 public void turnOn() { System.out.println("Audio를 켭니다."); } //turnOff() 추상 메소드의 실체 메소드 public void turnOff() { System.out.println("Audio를 끕니다."); } //setVolume() 추상 메소드의 실체 메소 public void setVolume(int volume) { if(volume>RemoteControl.MAX_VOLUME) { this.volume = RemoteControl.MAX_VOLUME; } else if(volume<RemoteControl.MIN_VOLUME) { this.volume = RemoteControl.MIN_VOLUME; } else { this.volume = volume; } System.out.println("현재 Audio 볼: " + this.volume); } } |
[ public 접근 제한 ]
구현 클래스에서 인터페이스의 추상 메소드에 대한 실체 메소드를 작성할 때 주의할 점은
인터페이스의 모든 메소드는 기본적으로 public 접근 제한을 갖기 때문에
public보다 더 낮은 접근 제한으로 작성할 수 없다는 것
- 구현 클래스가 작성되면 new 연산자로 객체를 생성할 수 있다. 다음 코드는 인터페이스를 사용한 것이 아님
- 인터페이스로 구현 객체를 사용하려면 다음과 같이 인터페이스 변수를 선언하고 구현 객체를 대입해야한다.
- 인터페이스 변수는 참조 타입이기 때문에 구현 객체가 대입될 경우 구현 객체의 번지를 저장
인터페이스 변수에 구현 객체 대입 // RemoteControlExample.java |
package sec01.exam04;
public class RemoteControlExample { public static void main(String[] args) { RemoteControl rc; rc = new Television(); rc = new Audio(); } } |
(2) 다중 인터페이스 구현 클래스
- 객체는 다음 그림과 같이 다수의 인터페이스 타입으로 사용 가능
- 인터페이스 A와 인터페이스 B가 객체의 메소드를 호출할 수 있으려면
객체는 두 인터페이스를 모두 구현해야한다.
인터페이스 // Searchable.java |
package sec01.exam05;
public interface Searchable { void search(String url); } |
다중 인터페이스 구현 클래스 // SmartTelevision.java |
package sec01.exam05;
public class SmartTelevision implements RemoteControl, Searchable { private int volume; public void turnOn() { System.out.println("TV를 켭니다."); } public void turnOff() { System.out.println("TV를 끕니."); } public void setVolume(int volume) { if(volume>RemoteControl.MAX_VOLUME) { this.volume = RemoteControl.MAX_VOLUME; } else if(volume<RemoteControl.MIN_VOLUME) { this.volume = RemoteControl.MIN_VOLUME; } else { this.volume = volume; } System.out.println("현재 TV 볼륨: " + this.volume); } public void search(String url) { System.out.println(url + "을 검색합니다."); } } |
인터페이스 변수에 구현 객체 대입 // SmartTelevisionExample.java |
package sec01.exam05;
public class SmartTelevisionExample { public static void main(String[] args) { SmartTelevision tv = new SmartTelevision(); RemoteControl rc = tv; Searchable searchable = tv; } } |
1)----------------
TV를 꼅니다. 현재 TV 볼륨: 5 2)---------------- Audio를 켭니다. 현재 Audio 볼륨: 5 3)---------------- Audio를 켭니다. 현재 Audio 볼륨: 5 4)---------------- TV를 꼅니다. 현재 TV 볼륨: 5 |
3) 인터페이스 사용
- 클래스를 선언할 때 인터페이스는 필드, 생성자 또는 메소드의 매개 변수, 생성자 또는 메소드의 로컬 변수로 선언될 수 있음
인터페이스 사용 // MyClass.java |
package sec01.exam06;
public class MyClass { // 필드 RemoteControl rc = new Television(); // 생성자 MyClass() { } MyClass(RemoteControl rc) { this.rc = rc; rc.turnOn(); rc.setVolume(5); } // 메소드 void methodA() { RemoteControl rc = new Audio(); rc.turnOn(); rc.setVolume(5); } void methodB(RemoteControl rc) { rc.turnOn(); rc.setVolume(5); } } |
인터페이스 사용 // MyClassExample.java |
package sec01.exam06;
public class MyClassExample { public static void main(String[] args) { System.out.println("1)----------------"); MyClass myClass1 = new MyClass(); myClass1.rc.turnOn(); myClass1.rc.setVolume(5); System.out.println("2)----------------"); MyClass myClass2 = new MyClass(new Audio()); System.out.println("3)----------------"); MyClass myClass3 = new MyClass(); myClass3.methodA(); System.out.println("4)----------------"); MyClass myClass4 = new MyClass(); myClass4.methodB(new Television()); } } |
1)----------------
TV를 꼅니다. 현재 TV 볼륨: 5 2)---------------- Audio를 켭니다. 현재 Audio 볼륨: 5 3)---------------- Audio를 켭니다. 현재 Audio 볼륨: 5 4)---------------- TV를 꼅니다. 현재 TV 볼륨: 5 |
Cat.java | Dog.java |
package sec01.verify.exam03;
public class Cat implements Soundable { @Override public String sound() { return "야옹"; } } |
package sec01.verify.exam03;
public class Dog implements Soundable { @Override public String sound() { return "멍멍"; } } |
2. 타입 변환과 다형성
- 개발할 때 인터페이스를 사용해서 메소드를 호출하면 구현 객체를 매우 손쉽고 빠르게 교체 가능
- 프로그램 소스 코드는 변함이 없는데, 구현 객체를 교체하면서 프로그램의 실행결과가 다양해짐
- 1 인터페이스를 이용해서 프로그램을 개발하고
- 1 인터페이스를 구현한 클래스로 처음에는 A 클래스를 선택했는데,
테스트를 해보니 A 클래스에 문제가 있다는 걸 알았을 때
B 클래스와 교체한 후 단 한 줄만 수정해서 프로그램을 재실행할 수 있
1) 자동 타입 변환 (promotion)
- 구현 객체가 인터페이스 타입으로 변환되는 것
- 프로그램 실행 도중에 자동적으로 타입 변환이 일어나는 것
- 인터페이스 구현 클래스를 상속해서 자식 클래스를 만들었다면
자식 객체 역시 인터페이스 타입으로 자동 타입 변환 가능
2) 필드의 다형성
- 한국 타이어와 금호 타이어는 공통적으로 타이어 인터페이스를 구현했기 때문에
모든 타이어 인터페이스에 있는 메소드를 가지고 있음 - 따라서 타이어 인터페이스로 동일하게 사용할 수 있는 교체 가능한 객체에 해당
인터페이스 Tire.java |
package sec02.exam01;
public interface Tire { public void roll(); } |
구현 클래스 HankookTire.java |
package sec02.exam01;
public class HankookTire implements Tire { @Override public void roll() { System.out.println("한국타이어가 굴러갑니다."); } } |
구현 클래스 KumhoTire.java |
package sec02.exam01;
public class KumhoTire implements Tire { @Override public void roll() { System.out.println("금호타이어가 굴러갑니다."); } } |
필드 다형성 Car.java |
package sec02.exam01;
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 |
package sec02.exam01;
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) 매개 변수의 다형성
- 자동 타입 변환은 필드의 값을 대입할 때도 발생하지만,
주로 메소드를 호출할 때 많이 발생한다. - 매개값을 다양화하기 위해서 상속에서는 매개 변수를 부모 타입으로 선언하고
호출할 때에는 자식 객체를 대입했다. - 이번에는 매개 변수를 인터페이스 타입으로 선언하고 호출할 때에는 구현 객체를 대입
- Bus가 구현 클래스라면 다음과 같이 Driver의 drive() 메소드를 호출할 때 Bus 객체를 생성해서 매개값으로 줄 수 있
매개 변수의 인터페이스화 // Driver.java |
package sec02.exam02;
public class DriverExample { public static void main(String[] args) { Driver driver = new Driver(); Bus bus = new Bus(); Taxi taxi = new Taxi(); driver.drive(bus); driver.drive(taxi); } } |
인터페이스 Vehicle.java |
package sec02.exam02;
public interface Vehicle { public void run(); } |
구현 클래스 Bus.java |
package sec02.exam02;
public class Bus implements Vehicle { @Override public void run() { System.out.println("버스가 달립니다."); } |
4) 강제 타입 변환
- 구현 객체가 인터페이스 타입으로 자동 타입 변환하면, 인터페이스에 선언된 메소드만 사용 가능하다.
- 인터페이스에는 3개의 메소드가 선언되어 있고 클래스에는 5개의 메소드가 선언되어 있다면
인터페이스로 호출 가능한 메소드는 3개뿐이다.
- 강제 타입 변환(casting)
- 경우에 따라서는 구현 클래스에 선언된 필드와 메소드를 사용해야 할 경우도 발생
- 이때 다시 구현 클래스 타입으로 변환한 다음, 구현 클래스의 필드와 메소드를 사용 가능
인터페이스 Vehicle.java |
package sec02.exam03;
public interface Vehicle { public void run(); } |
구현 클래스 Bus.java |
package sec02.exam03;
public class Bus implements Vehicle { @Override public void run() { System.out.println("버스가 달립니다."); } public void checkFare() { System.out.println("승차요금을 체크합니다."); } } |
강제 타입 변환 VehicleExample.java |
package sec02.exam03;
public class VehicleExample { public static void main(String[] args) { Vehicle vehicle = new Bus(); vehicle.run(); //vehicle.checkFare(); (x) Bus bus = (Bus) vehicle; bus.run(); bus.checkFare(); } } |
버스가 달립니다.
버스가 달립니다. 승차요금을 체크합니다. |
5) 객체 타입 확인
객체 타입 확인 Driver.java |
package sec02.exam04;
public class Driver { public void drive(Vehicle vehicle) { if(vehicle instanceof Bus) { Bus bus = (Bus) vehicle; bus.checkFare(); } vehicle.run(); } } |
객체 타입 확인 DriverExample.java |
package sec02.exam04;
public class DriverExample { public static void main(String[] args) { Driver driver = new Driver(); Bus bus = new Bus(); Taxi taxi = new Taxi(); driver.drive(bus); driver.drive(taxi); } } |
승차요금을 체크합니다.
버스가 달립니다. 택시가 달립니다. |
6) 인터페이스 상속
- 인터페이스도 다른 인터페이스를 상속할 수 있다.
- 인터페이스는 클래스와는 달리 다중 상속을 허용한다.
- 하위 인터페이스를 구현하는 클래스는 하위 인터페이스의 메소드뿐만 아니라
상위 인터페이스의 모든 추상 메소드에 대한 실체 메소드를 가지고 있어야한다.
- 하위 인터페이스로 타입 변환이 되면 상위 및 하위 인터페이스에 선언된 모든 메소드를 사용할 수 있으나,
상위 인터페이스로 타입 변환되면 상위 인터페이스에 선언된 메소드만 사용 가능하고 하위 인터페이스에 선언된 메소드는 사용할 수 없다.
상위 인터페이스 InterfaceA.java |
package sec02.exam05;
public interface InterfaceA { public void methodA(); } |
상위 인터페이스 InterfaceB.java |
package sec02.exam05;
public interface InterfaceB { public void methodB(); } |
하위 인터페이스 InterfaceC.java |
package sec02.exam05;
public interface InterfaceC extends InterfaceA, InterfaceB { public void methodC(); } |
하위 인터페이스 구현 ImplementationC.java |
package sec02.exam05;
public class ImplementationC 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-methodC() 실행"); } } |
호출 가능 메소드 Example.java |
package sec02.exam05;
public class Example { public static void main(String[] args) { ImplementationC impl = new ImplementationC(); InterfaceA ia = impl; ia.methodA(); System.out.println(); InterfaceB ib = impl; ib.methodB(); System.out.println(); InterfaceC ic = impl; ic.methodA(); ic.methodB(); ic.methodC(); } } |
ImplementationC-methodA() 실행
ImplementationC-methodB() 실행 ImplementationC-methodA() 실행 ImplementationC-methodB() 실행 ImplementationC-methodC() 실행 |
DataAccessObject.java | MySqlDao.java |
package sec02.verify.exam03;
public interface DataAccessObject { public void select(); public void insert(); public void update(); public void delete(); } |
package sec02.verify.exam03;
public class MySqlDao implements DataAccessObject { @Override public void select() { System.out.println("MySql DB에서 검색"); } @Override public void insert() { System.out.println("MySql DB에 삽입"); } @Override public void update() { System.out.println("MySql DB를 수정"); } @Override public void delete() { System.out.println("MySql DB에서 삭제"); } } |
OracleDao.java | DaoExample.java |
package sec02.verify.exam03;
public class OracleDao implements DataAccessObject { @Override public void select() { System.out.println("Oracle DB에서 검색"); } @Override public void insert() { System.out.println("Oracle DB에 삽입"); } @Override public void update() { System.out.println("Oracle DB를 수정"); } @Override public void delete() { System.out.println("Oracle DB에서 삭제"); } } |
package sec02.verify.exam03;
public class DaoExample { public static void dbWork(DataAccessObject dao) { dao.select(); dao.insert(); dao.update(); dao.delete(); } public static void main(String[] args) { dbWork(new OracleDao()); dbWork(new MySqlDao()); } } |
Chap 9. 중첩 클래스와 중첩 인터페이스
1. 중첩 클래스와 인터페이스 소개
- 중첩 클래스(nested class)
- 클래스 내부에 선언한 클래스
- 두 클래스의 멤버들을 서로 쉽게 접근할 수 있고, 외부에는 불필요한 관계 클래스를 감춰
코드의 복잡성을 줄일 수 있음
- 중헙 인터페이스(nested interface)
- 클래스 내부에 선언한 인터페이스
- 해당 클래스와 긴밀한 관계를 맺는 구현 클래스를 만들기 위
1) 중첩 클래스
- 클래스 내부에 선언되는 위치에 따라 두 가지로 분류
- 멤버 클래스
- 클래스의 멤버로서 선언되는 중첩 클래스
- 클래스나 객체가 사용 중이라면 언제든지 재사용이 가능
- 로컬 클래스
- 생성자 또는 메소드 내부에서 선언되는 중첩 클래스
- 메소드를 실행할 때만 사용되고 메소드가 종료되면 없어짐
선언 위치에 따른 분류 | 선언 위치 | 설명 | |
멤버 클래스 | 인스턴스 멤버 클래스 | class A { class B { ``` } } |
A 객체를 생성해야만 사용할 수 있는 B 클래스 |
정적 멤버 클래스 | class A { static class B { ``` } } |
A 클래스로 바로 접근할 수 있는 B 클래스 | |
로컬 클래스 | class A { void method() { class B{ ``` } } } |
method()가 실행할 때만 사용할 수 있는 B 클래스 |
- 중첩 클래스도 하나의 클래스이기 때문에 컴파일하면 바이트 코드 파일(.class)이 별도로 생성됨.
- 멤버 클래스일 경우 바이트 코드 파일의 이름은 다음과 같이 결정
- 로컬 클래스일 경우에는 다음과 같이 $1이 포함된 바이트 코드 파일이 생성
(1) 인스턴스 멤버 클래스
- 인스턴스 멤버 클래스는 static 키워드 없이 중첩 선언된 클래스
- 인스턴스 멤버 클래스는 인스턴스 필드와 메소드만 선언이 가능하고 정적 필드와 메소드는 선언할 수 없음
- A 클래스 외부에서 B 객체를 생성하려면 먼저 A 객체를 생성하고 B 객체를 생성해야 함.
- A 클래스 내부의 생성자 및 인스턴스 메소드에서는 일반 클래스처럼 B 객체를 생성할 수 있음
- 일반적으로 A 클래스 외부에서 B 객체를 생성하는 일은 거의 없음
- 대부분 A 클래스 내부에서 B 객체를 생성해서 사용
(2) 정적 멤버 클래스
- 정적 멤버 클래스는 static 키워드로 선언된 클래스를 말함
- 정적 멤버 클래스는 모든 종류의 필드와 메소드를 선언할 수 있음
- A 클래스 외부에서 정적 멤버 클래스 C의 객체를 생성하기 위해 A 객체를 생성할 필요가 없고, C 객체를 생성하면 됨
(3) 로컬 클래스
- 중첩 클래스는 메소드 내에서도 선언할 수 있음
- 로컬 클래스는 접근 제한자 및 static을 붙일 수 없음.
- 로컬 클래스는 메소드 내부에서만 사용되므로 접근을 제한할 필요가 없다.
- 로컬 클래스 내부에는 인스턴스 필드와 메소드만 선언할 수 있고 정적 필드와 메소드는 선언할 수 없음.
- 로컬 클래스는 메소드가 실행될 때 메소드 내에서 객체를 생성하고 사용해야함.
중첩 클래스 A.java |
package sec01.exam01;
/**바깥 클래스**/ class A { A() { System.out.println("A 객체가 생성됨"); } /**인스턴스 멤버 클래스**/ public class B { B() { System.out.println("B 객체가 생성됨"); } int field1; //static int field2; void method1() { } //static void method2() { } } /**정적 멤버 클래스**/ static class C { C() { System.out.println("C 객체가 생성됨"); } int field1; static int field2; void method1() { } static void method2() { } } void method() { /**로컬 클래스**/ class D { D() { System.out.println("D 객체가 생성됨"); } int field1; //static int field2; void method1() { } //static void method2() { } } D d = new D(); d.field1 = 3; d.method1(); } } |
중첩 클래스 객체 생성 Main.java |
package sec01.exam01;
public class Main { public static void main(String[] args) { A a = new A(); //인스턴스 멤버 클래스 객체 생성 A.B b = a.new B(); b.field1 = 3; b.method1(); //정적 멤버 클래스 객체 생성 A.C c = new A.C(); c.field1 = 3; c.method1(); A.C.field2 = 3; A.C.method2(); //로컬 클래스 객체 생성을 위한 메소드 호출 a.method(); } } |
A 객체가 생성됨
B 객체가 생성됨 C 객체가 생성됨 D 객체가 생성됨 |
2) 중첩 클래스의 접근 제한
- 멤버 클래스 내부에서 바깥 클래스의 필드와 메소드에 접근할 때는 제한이 따른다.
- 메소드의 매개 변수나 로컬 변수를 로컬 클래스에서 사용할 때도 제한이 따른다.
(1) 바깥 필드와 메소드에서 사용 제한
- 바깥 클래스에서 인스턴스 멤버 클래스를 사용할 때 제한이 있음
- 인스턴스 멤버 클래스는 바깥 클래스의 인스턴스 필드의 초기값이나 인스턴스 메소드에서 객체를 생성할 수 있음
- 정적 필드의 초기값이나 정적 메소드에서는 객체를 생성할 수 없음.
- 정적 멤버 클래스는 모든 필드의 초기값이나 모든 메소드에서 객체를 생성할 수 있음
바깥 필드와 메소드에서 사용 제한 A.java |
package sec01.exam02;
public class A { //인스턴스 필드 B field1 = new B(); C field2 = new C(); //인스턴스 메소드 void method1() { 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(); C var2 = new C(); } //인스턴스 멤버 클래스 class B {} //정적 멤버 클래스 static class C {} } |
(2) 멤버 클래스에서 사용 제한
- 멤버 클래스가 인스턴스 또는 정적으로 선언됨에 따라 멤버 클래스 내부에서 바깥 클래스의 필드와 메소드에 접근할 때에도 제한이 따름
- 인스턴스 멤버 클래스 안에서는 바깥 클래스의 모든 필드와 모든 메소드에 접근할 수 있지만,
정적 멤버 클래스 안에서는 바깥 클래스의 정적 필드와 메소드에만 접근할 수 있고
인스턴스 필드와 메소드에는 접근할 수 없음.
멤버 클래스에서 사용 제한 A.java |
package sec01.exam03;
public class A { int field1; void method1() { } static int field2; static void method2() { } class B { void method() { field1 = 10; method1(); field2 = 10; method2(); } } static class C { void method() { //field1 = 10; //method1(); field2 = 10; method2(); } } } |
(3) 로컬 클래스에서 사용 제한
- 메소드의 매개 변수나 로컬 변수를 로컬 클래스에서 사용할 때 제한이 있음
- 로컬 클래스의 객체는 메소드 실행이 종료되면 없어지는 것이 일반적이지만,
메소드가 종료되어도 계속 실행 상태로 존재할 수 있음 - 메소드를 실행하는 스레드와 다르므로 메소드가 종료된 후에도 로컬 스레드 객체는 실행 상태로 존재할 수 있음
로컬 클래스에서 사용 제한 Outter.java |
package sec01.exam04;
public class Outter { //자바 7 이전 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; } } } //자바 8 이후 public void method2(int arg) { int localVariable = 1; //arg = 100; (x) //localVariable = 100; (x) class Inner { public void method() { int result = arg + localVariable; } } } } |
(4) 중첩 클래스에서 바깥 클래스 참조 얻기
- 클래스 내부에서 this는 객체 자신의 참조
- 중첩 클래스에서 this 키워드를 사용하면 바깥 클래스의 객체 참조가 아니라, 중첩 클래스의 객체 참조가 됨.
- 따라서 중첩 클래스 내부에서 'this.필드, this.메소드()'로 호출하면 중첩 클래스의 필드와 메소드가 사용됨
- 중첩 클래스 내부에서 바깥 클래스의 객체 참조를 얻으려면 바깥 클래스의 이름을 this 앞에 붙여주면 됨.
중첩 클래스에서 바깥 클래스 참조 얻기 Outter.java |
package sec01.exam05;
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); this.method(); System.out.println(Outter.this.field); Outter.this.method(); } } } |
실행 클래스 OutterExample.java |
package sec01.exam05;
public class OutterExample { public static void main(String[] args) { Outter outter = new Outter(); Outter.Nested nested = outter.new Nested(); nested.print(); } } |
Nested-field
Nested-method Outter-field Outter-method |
3) 중첩 인터페이스
- 중첩 인터페이스는 클래스의 멤버로 선언된 인터페이스를 말함.
- 인터페이스를 클래스 내부에 선언하는 이유는 해당 클래스와 긴밀한 관계를 맺는 구현 클래스를 만들기 위해서임.
- 중첩 인터페이스는 인스턴스 멤버 인터페이스와 정적 멤버 인터페이스 모두 가능
- 인스턴스 멤버 인터페이스는 바깥 클래스의 객체가 있어야 사용이 가능하며,
정적 멤버 인터페이스는 바깥 클래스의 객체 없이 바깥 클래스만으로 바로 접근 가능 - 주로 정적 멤버 인터페이스를 많이 사용하는데 UI 프로그래밍에서 이벤트를 처리할 목적으로 많이 활용됨.
중첩 인터페이스 Button.java |
package sec01.exam06;
public class Button { OnClickListener listener; void setOnClickListener(OnClickListener listener) { this.listener = listener; } void touch() { listener.onClick(); } static interface OnClickListener { void onClick(); } } |
구현 클래스 CallListener.java |
package sec01.exam06;
public class CallListener implements Button.OnClickListener { @Override public void onClick() { System.out.println("전화를 겁니다."); } } |
구현 클래스 MessageListener.java |
package sec01.exam06;
public class MessageListener implements Button.OnClickListener { @Override public void onClick() { System.out.println("메시지를 보냅니다."); } } |
버튼 이벤트 처리 ButtonExample.java |
package sec01.exam06;
public class ButtonExample { public static void main(String[] args) { Button btn = new Button(); btn.setOnClickListener(new CallListener()); btn.touch(); btn.setOnClickListener(new MessageListener()); btn.touch(); } } |
전화를 겁니다.
메시지를 보냅니다. |
[ 선택 미션 ]
package sec01.verify.exam05;
public class BackgroundChangeListener implements CheckBox.OnSelectListener { @Override public void onSelect() { System.out.println("배경을 변경합니다."); } } |
2. 익명 객체
- 이름이 없는 객체를 말한다.
- 어떤 클래스를 상속하거나 인터페이스를 구현해야만 함.
- 일반적으로 명시적으로 클래스 이름을 주고 선언하는 방법을 사용
- 이 경우 부모 클래스 변수는 클래스이름1의 객체를 참조하고, 인터페이스 변수는 클래스이름2의 객체를 참조
- 그러나 익명 객체를 생성할 때는 다음과 같이 클래스 이름이 없음
- 이 경우 부모 클래스 변수는 이름이 없는 자식 객체를 참조하고, 인터페이스 변수는 이름이 없는 구현 객체를 참조
1) 익명 자식 객체 생성
- 보모 타입의 필드 또는 변수를 선언하고 자식 객체를 초기값으로 대입하는 경우를 생각해보자
- 우선 부모 클래스를 상속해서 자식 클래스를 선언한다.
- 그리고 new 연산자를 이용해서 자식 객체를 생성한 후 부모 타입의 필드 또는 변수에 대입
부모 클래스 Person.java |
package sec02.exam01;
public class Person { void wake() { System.out.println("7시에 일어납니다."); } } |
익명 자식 객체 생성 Anonymous.java |
package sec02.exam01;
public class Anonymous { //필드 초기값으로 대입 Person field = new Person() { void work() { System.out.println("출근합니다."); } @Override void wake() { System.out.println("6시에 일어납니다."); work(); } }; void method1() { //로컬 변수값으로 대입 Person localVar = new Person() { void walk() { System.out.println("산책합니다."); } @Override void wake() { System.out.println("7시에 일어납니다."); walk(); } }; //로컬 변수 사용 localVar.wake(); } void method2(Person person) { person.wake(); } } |
익명 자식 객체 생성 AnonymousExample.java |
package sec02.exam01;
public class AnonymousExample { public static void main(String[] args) { Anonymous anony = new Anonymous(); //익명 객체 필드 사용 anony.field.wake(); //익명 객체 로컬 변수 사용 anony.method1(); //익명 객체 매개값 사용 anony.method2( new Person() { void study() { System.out.println("공부합니다."); } @Override void wake() { System.out.println("8시에 일어납니다."); study(); } } ); } } |
6시에 일어납니다.
출근합니다. 7시에 일어납니다. 산책합니다. 8시에 일어납니다. 공부합니다. |
2) 익명 구현 객체 생성
- 인터페이스 타입의 필드 또는 변수를 선언하고, 구현 객체를 초기값으로 대입하는 경우를 생각해보자.
- 구현 클래스를 선언한 후, new 연산자를 이용해 구현 객체를 생성한 후
인터페이스 타입의 필드 또는 로컬 변수에 대입
인터페이스 RemoteControl.java |
package sec02.exam02;
public interface RemoteControl { public void turnOn(); public void turnOff(); } |
익명 구현 객체 생성 Anonymous.java |
package sec02.exam02;
public class Anonymous { //필드 초기값으로 대입 RemoteControl field = new RemoteControl() { @Override public void turnOn() { System.out.println("TV를 켭니다."); } @Override public void turnOff() { System.out.println("TV를 끕니다."); } }; void method1() { //로컬 변수값으로 대입 RemoteControl localVar = new RemoteControl() { @Override public void turnOn() { System.out.println("Audio를 켭니다."); } @Override public void turnOff() { System.out.println("Audio를 끕니다."); } }; //로컬 변수 사용 localVar.turnOn(); } void method2(RemoteControl rc) { rc.turnOn(); } } |
익명 구현 객체 생성 AnonymousExample.java |
package sec02.exam02;
public class AnonymousExample { public static void main(String[] args) { Anonymous anony = new Anonymous(); //익명 객체 필드 사용 anony.field.turnOn(); //익명 객체 로컬 변수 사용 anony.method1(); //익명 객체 매개값 사용 anony.method2( new RemoteControl() { @Override public void turnOn() { System.out.println("SmartTV를 켭니다."); } @Override public void turnOff() { System.out.println("SmartTV를 끕니다."); } } ); } } |
TV를 켭니다.
Audio를 켭니다. SmartTV를 켭니다. |
UI 클래스 Button.java |
package sec02.exam03;
public class Button { OnClickListener listener; void setOnClickListener(OnClickListener listener) { this.listener = listener; } void touch() { listener.onClick(); } static interface OnClickListener { void onClick(); } } |
UI 클래스 Window.java |
package sec02.exam03;
public class Window { Button button1 = new Button(); Button button2 = new Button(); //필드 초기값으로 대입 Button.OnClickListener listener = new Button.OnClickListener() { @Override public void onClick() { System.out.println("전화를 겁니다."); } }; Window() { button1.setOnClickListener(listener); //매개값으로 필드 대입 button2.setOnClickListener(new Button.OnClickListener() { @Override public void onClick() { System.out.println("메시지를 보냅니다."); } }); } } |
실행 클래스 Main.java |
package sec02.exam03;
public class Main { public static void main(String[] args) { Window w = new Window(); w.button1.touch(); w.button2.touch(); } } |
전화를 겁니다.
메시지를 보냅니다. |
3) 익명 객체의 로컬 변수 사용
- 메소드의 매개 변수나 로컬 변수를 익명 객체 내부에서 사용할 때도 제한이 있다.
- 익명 객체는 메소드 실행이 종료되면 없어지는 것이 일반적이지만,
메소드가 종료되어도 계속 실행 상태로 존재할 수 있음 - 문제는 메소드의 매개 변수나 로컬 변수를 익명 객체 내부에서 사용할 때이다.
- 매개 변수나 로컬 변수는 메소드 실행이 끝나면 스택 메모리에서 사라지기 때문에
익명 객체에서 지속적으로 사용할 수 없다.
인터페이스 Calculatable.java |
package sec02.exam04;
public interface Calculatable { public int sum(); } |
익명 객체의 로컬 변수 사용 Anonymous.java |
package sec02.exam04;
public class Anonymous { private int field; public void method(final int arg1, int arg2) { final int var1 = 0; int var2 = 0; field = 10; //arg1 = 20; (x) //arg2 = 20; (x) //var1 = 30; (x) //var2 = 30; (x) Calculatable calc = new Calculatable() { @Override public int sum() { int result = field + arg1 + arg2 + var1 + var2; return result; } }; System.out.println(calc.sum()); } } |
익명 객체의 로컬 변수 사용 AnonymousExample.java |
package sec02.exam04;
public class AnonymousExample { public static void main(String[] args) { Anonymous anony = new Anonymous(); anony.method(0, 0); } } |
10 |
[ 확인 문제 9-2-1 ]
package sec02.verify.exam01;
public class Worker { public void start() { System.out.println("쉬고 있습다."); } } |
package sec02.verify.exam01;
public class Anonymous { Worker field = new Worker() { @Override public void start() { System.out.println("디자인을 합니다."); } }; void method1() { Worker localVar = new Worker() { @Override public void start() { System.out.println("개발을 합니다."); } }; localVar.start(); } void method2(Worker worker) { worker.start(); } } |
package sec02.verify.exam01;
public class AnonymousExample { public static void main(String[] args) { Anonymous anony = new Anonymous(); anony.field.start(); anony.method1(); anony.method2( new Worker() { @Override public void start() { System.out.println("테스트를 합니다."); } } ); } } |
[ 확인 문제 9-2-2 ]
package sec02.verify.exam02;
public interface Vehicle { public void run(); } |
package sec02.verify.exam02;
public class Anonymous { Vehicle field = new Vehicle() { @Override public void run() { System.out.println("자전거가 달립니다."); } }; void method1() { Vehicle localVar = new Vehicle() { @Override public void run() { System.out.println("승용차가 달립니다."); } }; localVar.run(); } void method2(Vehicle v) { v.run(); } } |
package sec02.verify.exam02;
public class AnonymousExample { public static void main(String[] args) { Anonymous anony = new Anonymous(); anony.field.run(); anony.method1(); anony.method2( new Vehicle() { @Override public void run() { System.out.println("트럭이 달립니다."); } } ); } } |
[ 확인 문제 9-2-3 ]
package sec02.verify.exam03;
public class CheckBox { OnSelectListener listener; void setOnSelectListener(OnSelectListener listener) { this.listener = listener; } void select() { listener.onSelect(); } static interface OnSelectListener { void onSelect(); } } |
package sec02.verify.exam03;
public class CheckBoxExample { public static void main(String[] args) { CheckBox checkBox = new CheckBox(); checkBox.setOnSelectListener(new CheckBox.OnSelectListener() { @Override public void onSelect() { System.out.println("배경을 변경합니다."); } }); checkBox.select(); } } |
728x90
반응형
'혼공 스터디 > 혼자 공부하는 자바' 카테고리의 다른 글
[혼공자바] 6주차: 예외와 API 클래스 (0) | 2024.02.06 |
---|---|
[ 혼공자바 ] 4주차 : 상속 (1) | 2024.01.26 |
[ 혼공자바 ] 3주차 : 클래스 (0) | 2024.01.19 |
[ 혼공자바 ] 2주차 : 조건문, 반복문, 참조 타입 (1) | 2024.01.14 |
[ 혼공자바 ] 1주차 (1) | 2024.01.07 |