일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 |
- React
- MVC
- 자료구조
- JDBC
- rpa
- 이클립스
- View
- spring
- mysql
- html
- 조건문
- 상속
- Oracle
- jsp
- Scanner
- Board
- string
- Eclipse
- db
- SpringBoot
- jquery
- Controller
- 배열
- 문자열
- Thymeleaf
- Java
- API
- Array
- Uipath
- Database
- Today
- Total
유정잉
8일차 Java [ 인터페이스 ] ☆★ 본문
class : 필드 생성자 메소드
interface : 필드 메소드 => 생성자 없음 즉, 객체 생성 불가 !!! ( 객체를 굳이 main에서만 생성할 필요는 x )
( 필드 => 상수값 public static final 생략 ) [ public static final ] 타입 상수명 = 값;
( 추상메소드 => public abstract 생략 turnOn();끝 메소드 구현불가 [ public abstract ] turnOn(); )
(추상메소드 아닐 경우 => 앞에 static, default, private를 붙여 적게되면 메소드 구현할 수 있다.)
→ interface를 @Override할 경우 부모 interface에 있는 모든 메소드 상속받아야 함.
→ interface에서 @Override 받을 땐 앞에 public 붙여 줘야함 (?)
→ abstract class Phone 에서는 { public abstract String turnOn(); } 생략 불가 interface만 abstract 생략 가능
→ class로 상속 받을 땐 implements, interface로 상속 받을 땐 extends
[ 인터페이스 역할 ] ☆★
인터페이스 ( interface ) 는 사전적인 의미로 두 장치를 연결하는 접속기를 말한다. 여기서 두 장치를 서로 다른 객체로 본다면, 인터페이스는 이 두 객체를 연결하는 역할을 한다. 다음 그림과 같이 객체 A는 인터페이스를 통해 객체 B를 사용할 수 있다. 객체 A가 인터페이스의 메소드를 호출하면, 인터페이스는 객체 B의 메소드를 호출하고 그 결과를 받아 객체 A로 전달해준다. 객체 A가 객체 B의 메소드를 직접 호출하면 간단할텐데 왜 중간에 인터페이스를 거치도록 하는 것일까? 객체 B가 객체 C로 교체 된다고 가정해보자. 객체 A는 인터페이스의 메소드만 사용하므로 객체 B가 객체 C로 변경된 것에 관심이 없다. 만약 인터페이스 없이 객체 A가 객체 B를 직접 사용한다면 객체 A의 소스 코드를 객체 B에서 객체 C로 변경하는 작업이 추가로 필요할 것이다. 객체 A가 인터페이스의 메소드를 호출하면 실제로 실행되는 것은 인터페이스 뒤편에 객체 B 또는 객체 C의 메소드이다. 만약 객체 B의 메소드 실행 결과와 객체 C의 메소드 실행결과가 다르다면, 객체 A는 객체 교체로 인해 다른 결과를 얻게 된다. 이 특징으로 인해 인터페이스는 다형성 구현에 주된 기술로 이용된다. 상속을 이용해서 다형성을 구현할 수도 있지만, 인터페이스를 이용해서 다형성을 구현하는 경우가 더 많다.
[ 인터페이스와 구현 클래스 선언 ]
인터페이스는 '~.java' 형태의 소스 파일로 작성되고 '~.class'형태로 컴파일되기 때문에 물리적인 형태는 클래스와 동하다. 단 소스를 작성할 때 선언하는 방법과 구성 멤버가 클래스와 다르다.
※ 인터페이스 선언 : 인터페이스 선언은 class 키워드 대신 interface 키워드를 사용한다. 접근 제한자로는 클래스와 마찬가지로 같은 패키지 내에서만 사용 가능한 default, 패키지와 상관없이 사용 가능한 public을 붙일 수 있다.
interface 인터페이스명 { ... } // => defalut 접근 제한
public interface 인터페이스명 { ... } // => public 접근 제한
중괄호 안에는 인터페이스가 가지는 멤버들을 선언할 수 있는데, 다음과 같은 종류가 있다.
public interface 인터페이스명 {
// public 상수 필드
// public 추상 메소드
// public 디폴트 메소드
// public 정적 메소드
// public 메소드
// private 정적 메소드
}
※ 이클립스에서 인터페이스 선언하는 법 : Package Explorer -> Project File -> 오른쪽 버튼 클릭 -> [ New ] -> [ Interface ]
※ 구현 클래스 선언 : 객체 A가 인터페이스의 추상 메소드를 호출하면 인터페이스는 객체 B의 메소드를 실행한다. 그렇다면 객체 B는 인터페이스에 선언된 추상 메소드와 동일한 선언부를 가진 ( 재정의된 ) 메소드를 가지고 있어야 한다. 여기서 객체 B를 인터페이스를 구현한 ( implement ) 객체라고 한다. 인터페이스에 정의된 추상 메소드에 대한 실행 내용이 구현(작성)되어 있기 때문이다. 객체 B와 같은 구현객체는 다음과 같이 인터페이스를 구현하고있음을 선언부에 명시해야 한다.
public class B implements 인터페이스명 { ... }
implements 키워드는 해당 클래스가 인터페이스를 통해 사용할 수 있다는 표시이며, 인터페이스의 추상 메소드를 재정의한 메소드가 있다는 뜻이다. 앞에서 선언한 RemoteControl 인터페이스로 Television을 사용할 수 있도록 Televisod 구현 슬래스를 선언해보자.
※ 변수 선언과 구현 객체 대입 : 인터페이스도 하나의 타입이므로 변수의 타입으로 사용할 수 있다. 인터페이스는 참조 타입에 속하므로 인터페이스 변수에는 객체를 참조하고 있지 않다는 뜻으로 null을 대입할 수 있다.
RemoteControl rc;
RemoteControl rc = null;
인터페이스를 통해 구현 객체를 사용하려면, 인터페이스 변수에 구현 객체를 대입해야 한다. 정확히 말하면 구현 객체의 번지를 대입해야 한다. 다음 Television 객체를 생성하고 번지를 대입하는 코드이다.
rc = new Television();
만약 Television이 implements RemoteControl로 선언되지 않았다면 RemoteControl이 타입의 변수 re에 대입할 수 없다. 인터페이스 변수에 구현 객체가 대입이 되었다면 변수를 통해 인터페이스의 추상 메소드를 호출할 수 있게 된다.
rc.turnOn();
인터페이스 변수를 통해 turnOn() 메소드가 호출되면, 실제로 실행되는 것은 TElevision에서 재정의된 turnON() 메소드이다. 이를 확인하기 위해 다음과 같이 RemoteControlExample을 작성하고 실행해보자.
5~6라인처럼 변수를 먼저 선언한 다음에 구현 객체를 대입해도 되지만, 다음과 같이 변수 선언과 동시에 구현 객체를 대입할 수도 있다.
RemoteControl rc = new Television();
rc 변수에는 RemoteControl을 구현한 어떠한 객체든 대입이 가능하다. 만약 Audio 객체가 구현 객체라면 다음과 같이 Audio 객체로 교체해도 대입할 수 있다,
rc = new Audio();
rc.turnOn();
이 경우 실제 실행 되는 것은 Audio에서 재정의된 turnOn() 메소드이다. 확인하기 위해 다음과 같이 Audio 클래스를 생성해보자.
public static Audio implements RemoteControl {
public static void main(String[] args) {
@Override
System.out.println("Audio를 켭니다.");
public void turnOn();
}
}
그리고 RemoteControlExample을 다음과 같이 수정하고 실행해보자.
public class RemoteControlExample {
public static void main(String[] args) {
RemoteControl rc;
// rc변수에 Television 객체를 대입
rc = new Television();
rc.turnOn();
// rc변수에 Audio 객체를 대입 ( 교체시킴 )
rc = new Audio();
rc.turnOn();
}
}
[ 상수 필드 ]
인터페이스는 public static final 특성을 갖는 불변의 상수 필드를 멤버로 가질 수 있다. 인터페이스에 선언된 필드는 모두 public static final 특성을 갖기 때문에 public static final을 생략하더라도 자동적으로 컴파일 과정에서 붙게 된다. 상수명은 대문자로 작성하되, 서로 다른 단어로 구성되어 있을 경우 언더바 ( _ ) 로 연결하는 것이 관례이다. 예를 들어 RemoteControl 인터페이스에 MAX_VALUE와 MIN_VALUE 상수를 선언해보자.
[ public static final ] 타입 상수명 = 값;
ex) int MAX_VOLUME = 100;
[ 추상 메소드 ]
인터페이스는 구현 클래스가 재정의해야 하는 public 추상 메소드 ( abstract method )를 멤버로 가질 수 있다. 추상 메소드는 리턴 타입, 메소드명, 매개변수만 기술되고 중괄호 { } 를 붙이지 않는 메소드를 말한다. public abstract를 생략하더라도 컴파일 과정에서 자동으로 붙게 된다. 추상 메소드는 객체 A가 인터페이스를 통해 어떻게 메소드를 호출할 수 있는지 방법을 알려주는 역할을 한다. 인터페이스 구현 객체 B는 추상 메소드의 실행부를 갖는 재정의된 메소드가 있어야 한다.
[ public abstract ] 리턴타입 메소드명 ( 매개변수, ... ) ;
ex) public void turnOn();
※ 이클립스에서 인터페이스 추상 메소드를 자동으로 재정의 해주는 기능 :
이클립스 메뉴 -> [ Source ] -> [ Override/Implement Methos ] 를 선택 -> 추상메소드를 체크한 후 [ OK ] 버튼 클릭
[ 디폴트 메소드 ]
인터페이스에는 완전한 실행 코드를 가진 디폴트 메소드를 선언할 수 있다. 추상 메소드는 실행부 ( 중괄호 { } ) 가 없지만, 디폴트 메소드는 실행부가 있다. 선언 방법은 클래스 메소드와 동일한데, 차이점은 default 키워드가 리턴 타입 앞에 붙는다.
[ public ] default 리턴타입 메소드명 ( 매개변수, ... ) { ... }
[ 정적 메소드 ]
인터페이스에는 정적 메소드 선언이 가능하다. 추상 메소드와 디폴트 메소드는 구현 객체가 필요하지만, 정적 메소드는 구현 객체가 없어도 인터페이스만으로 호출할 수 있다. 선언 방법은 클래스 정적 메소드와 완전 동일하다. 단, public을 생략하더라도 자동으로 컴파일 과정에서 붙는 것이 차이점이다.
[ public : private ] static 리턴타입 메소드명 ( 매개변수, ... ) { ... }
[ private 메소드 ] ( 거의 쓰이지 않음 )
인터페이스 상수 필드, 추상 메소드, 디폴트 메소드, 정적 메소드는 모두 public 접근 제한을 갖는다. 이 멤버들을 선언할 때는 public을 생략하더라도 컴파일 과정에서 public 접근 제한자가 붙어 항상 외부에서 접근이 가능하다. 또한 인터페이스에 외부에서 접근할 수 없는 private 메소드 선언도 가능하다.
구분 | 설명 |
private 메소드 | 구현 객체가 필요한 메소드 |
private 정적 메소드 | 구현 객체가 필요 없는 메소드 |
[ 다중 인터페이스 구현 ]
구현 객체는 여러 개의 인터페이스를 implements할 수 있다. 구현 객체가 인터페이스 A와 인터페이스 B를 구현하고 있다면, 각각의 인터페이스를 통해 구현 객체를 사용할 수 있다. 구현 클래스는 다음과 같이 인터페이스 A와 인터페이스 B를 implements 뒤에 쉼표로 구분해서 작성해, 모든 인터페이스가 가진 추상 메소드를 재정의해야 한다.
public class 구현클래스명 implements 인터페이스 A, 인터페이스 B { // => 여러개 상속받을 수 있다
// 모든 추상 메소드 재정의
}
※ interface 다중 상속 받을 때 : class로 상속 받을 땐 implements interface1,2로 받고
interface로 상속 받을 땐 extends interface1,2 (부모인터페이스1,2) 로 받는다.
[ 인터페이스 상속 ]
인터페이스도 다른 인터페이스를 상속할 수 있으며, 클래스와는 달리 다중 상속을 허용한다. 다음과 같이 extends 키워드 뒤에 상속할 인터페이스들을 나열하면 된다.
public interface 자식인터페이스 extends 부모인터페이스1, 부모인터페이스2 { ... } => 여러개 상속받을 수 있다
자식 인터페이스의 구현 클래스는 자식 인터페이스의 메소드 뿐만 아니라 부모 인터페이스의 모든 추상 메소드를 재정의해야 한다. 그리고 구현 객체는 다음과 같이 자식 및 부모 인터페이스 변수에 대입될 수 있다.
자식인터페이스 변수 = new 구현클래스 ( ... );
자식인터페이스1 변수 = new 구현클래스 ( ... );
자식인터페이스2 변수 = new 구현클래스 ( ... );
구현 객체가 자식 인터페이스 변수에 대입되면 자식 및 부모 인터페이스의 추상 메소드를 모두 호출할 수 있으나, 부모 인터페이스 변수에 대입되면 부모 인터페이스에 선언된 추상 메소드만 호출 가능하다.
A B
| /
C
|
D
[ 인터페이스 타입 변환 ]
인터페이스의 타입 변환은 인터페이스와 구현 클래스 간에 발생한다. 인터페이스의 변수에 구현 객체를 대입하면 구현 객체는 인터페이스 타입으로 자동 타입 변환된다. 반대로 인터페이스 타입을 구현 클래스 타입을 변환시킬 수 있는데, 이때는 강제 타입 변환이 필요하다.
※ 자동 타입 변환 : 자동 타입 변환 ( promotion )은 의미 그대로 자동으로 타입 변환이 일어나는 것을 말한다. 자동 타입 변환은 다음 조건에서 일어난다.
인터페이스 변수 = 구현객체; // => 구현객체가 인터페이스 변수로 자동 타입 변환
부모 클래스가 인터페이스를 구현하고 있다면 자식 클래스도 인터페이스 타입으로 자동 타입 변환될 수 있다. 인터페이스 A를 구현한 B,C 클래스가 있고 B를 상속한 D클래스, C를 상속한 E 클래스가 있다. B, C, D, E로부터 생성된 객체는 모두 인터페이스 A로 자동 타입 변환될 수 있다. 모두 인터페이스 A를 직,간접적으로 구현하고 있기 때문이다.
인터페이스 A B b = new B(); C c = new C(); D d = new D(); E e = new E();
| | A a; a = b; a = c; a = d; a = e; ==> 모두 가능
구현클래스 B 구현클래스 C
| |
자식클래스 D 자식클래스 E
※ 강제 타입 변환 : 강제 타입 변환 캐스팅 ( Casting ) 기호를 사용해서 인터페이스 타입을 구현 클래스 타입으로 변환 시키는 것을 말한다.
구현클래스 변수 = (구현클래스) 인터페이스변수; // => 인터페이스변수가 구현클래스변수로 강제 타입 변환
구현 객체가 인터페이스 타입으로 자동 변환되면, 인터페이스에 선언된 메소드만 사용 가능하다. RemoteControl 인터페이스에는 3개의 메소드, Television 클래스에는 5개의 메소드가 선언되어 있다면 RemoteControl 인터페이스로 호출 가능한 메소드는 3개뿐이다.
RemoteControl Television
turnOn(); trnOn() { ... } // 위에세개만호출가능 turnOn,turnOff,setVolume
turnOff(); turnOff(); { ... }
setVolume(int volume); setVolume(int vloume) { ... }
setTime() { ...}
record() { ... }
자동 타입 변환 후 Television의 setTime()과 record() 메소드를 호출하고 싶다면 캐스팅 기호를 사용해서 원래 Television으로 강제 타입 변환해야 한다.
RemoteControl rc = new Television(); Television tv = new (Television) rc;
rc.turnOn(); tv. turnOn() { ... }
rc.turnOff(); tv.turnOff(); { ... }
rc.setVolume(10); tv.setVolume(10) { ... }
tv.setTime() { ... }
tv.record() { ... }
[ 다형성 ]
인터페이스 또한 다형성을 구현하는 주된 기술로 사용된다. 현업에서는 상속보다는 인터페이스를 통해서 다형성을 구현하는 경우가 더 많다. 다형성이란 사용방법은 동일하지만 다양한 결과가 나오는 성질을 말한다. 다음 그림에서 구현 객체 B와 객체 C 둘 중 어느 객체가 인터페이스에 대입되었느냐에 따라서 객체 A의 메소드 호출 결과는 달라질 수 있다. 상속의 다형성과 마찬가지로 인터페이스 역시 다형성을 구현하기 위해 재정의와 자동 타입 변환 기능을 이용한다.
메소드 재정 + 자동 타입 변환 = 다형성
인터페이스의 추상 메소드는 구현 클래스에서 재정의 해야 하며, 재정의되는 내용은 구현 클래스 마다 다르다. 구현 객체는 인터페이스 타입으로 자동 타입 변환이 되고, 인터페이스 메소드 호출 시 구현 객체의 재정의된 메소드가 호출되어 다양한 실행 결과를 얻을 수 있다.
※ 필드의 다형성 : 상속에서는 부모 타이어를 클래스 타입에 자식 객체인 한국 타이어 또는 금호 타이어를 대입해서 다형성을 보여주었지만, 이번엔 부모 타입이 클래스 타입이 아니고 인터페이스라는 점이 다르다. 자동차를 설계할 때 다음과 같이 필드 타입으로 타이어 인터페이스를 선언하면 필드값으로 한국 타이어 또는 금호 타이어 객체를 대입할 수 있다. 자동 타입 변환 때문이다. ( 자동차는 동일한 타이어 인터페이스로 한국 타이어와 금호 타이어를 사용하지만 각 타이어의 성능은 다르게 나온다 = 다형성 )
public class Car P
Tire tire1 = new HankookTire();
Tire tire2 = new KumhoTire();
}
Car 객체를 생성한 후 다른 구현 객체를 대입할 수도 이다. 이것이 타이어 교체에 해당된다.
Car myCar = new Car();
myCar.tire1 = new KumhoTire();
tire1과 tire2필드에 어떠한 타이어 구현 객체가 대입되어도 Car 객체는 타이어 인터페이스에 선언된 메소드만 사용하므로 전혀 문제가 되지 않는다.
※ 매개변수의 다형성 : 메소드 호출 시 매개값을 다양화하기 위해 상속에서는 매개변수 타입을 부모 타입으로 선언하고 호출할 때에는 다양한 자식 객체를 대입했다. 이것은 자동 타입 변환 떄문인데, 비슷한 원리로 매개변수 타입을 인터페이스로 선언하면 메소드 호출 시 다양한 구현 객체를 대입할 수 있다.
[ 객체 타입 확인 ]
우리는 상속에서 객체 타입을 확인하기 위해 instanceof 연산자를 사용했는데, 인터페이스에서도 사용할 수 있다. 예를 들어 Vehicle 인터페이스 변수에 대입된 객체가 Bus 인지 확인하는 코드는 다음과 같다.
if ( vehicle instanceof Bus ) {
// vehicle에 대입된 객체가 Bus 일 경우 실행
}
메소드의 매개변수가 인터페이스 타입일 경우, 메소드 호출 시 매개값은 해당 인터페이스를 구현하는 모든 객체가 될 수 있다. 만약 매개값이 특정 구현 객체일 경우에만 강제 타입 변환을 하고 싶다면 instanceof 연산자를 사용해서 매개값의 타입을 검사해야 한다.
public void method ( Vehicle vehicle ) {
if ( vehicle instanceof Bus ) {
Bus bus = ( Bus ) vehicle; // bus 변수 사용
}
}
Java12 부터는 instanceof 연산의 결과가 true일 경우, 우측 타입 변수를 사용할 수 있기 때문에 강제 타입 변환이 필요 없다.
if ( vehicle instanceof Bus bus ) {
// bus 변수 사용
}
[ 봉인된 인터페이스 ]
java 15 부터는 무분별한 자식 인터페이스 생성을 방지하기 위해 봉인된 ( sealed ) 인터페이스를 사용할 수 있다.
[ 복습 연습 ]
☆ 지금까지 배운 모든 걸 활용한 코드 ☆ 이해해보기 !!!
import java.util.Scanner;
class PhoneNum{
String name;
String phone;
PhoneNum(String n, String p){
name=n;
phone=p;
}
void show() {
System.out.println("이름 : "+name);
System.out.println("번호 : "+phone);
}
}
class School extends PhoneNum{
String major;
School(String n,String p,String major){
super(n,p);
this.major=major;
}
@Override
void show() {
super.show(); //System.out.println("이름 : "+name);
//System.out.println("번호 : "+phone);
System.out.println("전공 : "+major);
}
}
class Worker extends PhoneNum{
String grade;
Worker(String n, String p, String grade){
super(n,p);
this.grade=grade;
}
@Override
void show() {
super.show(); //System.out.println("이름 : "+name);
//System.out.println("번호 : "+phone);
System.out.println("직급 : "+grade);
}
}
class Arr{
PhoneNum [] ary; //객체 배열
int n;
Arr(int n){ //생성자 역할: 초기화 작업
ary=new PhoneNum[n]; //PhoneNum [] ary=new PhoneNum[5];
n=0;
}
void add(PhoneNum p) {
ary[n++]=p; //친구추가
}
void friend(char ch) {
Scanner s=new Scanner(System.in);
System.out.println("이름: ");
String name=s.next();
System.out.println("번호: ");
String num=s.next();
switch(ch) {
case 'A': //학교친구 입력
System.out.println("전공: ");
String major=s.next();
add(new School(name, num, major));
//PhoneNum p = new School(name, num, major);
break;
case 'B': //회사동료 입력
System.out.println("직급: ");
String grade=s.next();
add(new Worker(name, num, grade));
//PhoneNum p = new Worker(name, num, grade);
break;
}
}
void all() {
for(int i=0;i<n;i++) {
ary[i].show();
}
}
}
public class Test {
public static void main(String[] args) {
Arr ar=new Arr(5);
//객체 배열 5개
while(true) {
System.out.println("A. 학교 친구");
System.out.println("B. 직장 동료");
System.out.println("C. 종료");
System.out.println("D. 출력");
System.out.println("문자입력");
Scanner s=new Scanner(System.in);
char c=s.next().charAt(0); //A~D중 하나 입력
switch(c) {
case 'A': //문자 A를 입력했으면
ar.friend(c); //
break;
case 'B': //문자 B를 입력했으면
ar.friend(c); //
break;
case 'C': //문자 C를 입력했으면
System.out.println("종료");
return;
case 'D':
ar.all();
}
}
}
}
'네이버 클라우드 부트캠프 > 복습 정리' 카테고리의 다른 글
10일차 Java [ java.base 모듈, 멀티 스레드 ] (0) | 2024.03.05 |
---|---|
9일차 Java [ 예외 처리 ] (0) | 2024.03.04 |
7일차 Java [ 상속, 객체배열 ] ☆★ (4) | 2024.02.28 |
6일차 Java [ 싱글톤, Getter Setter, 정적 멤버, final, 패키지 ] (0) | 2024.02.27 |
5일차 Java [ 객체 지향 프로그램 , 클래스, 접근 제한자 ] ☆★ (2) | 2024.02.26 |