일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 스프링 모달창
- 상속
- AOP란?
- 추상 메서드
- 형변환
- 오라클 비교연산자
- singleton
- 이클립스 오라클 연동
- downcasting
- static 예제
- oracle 연동
- Servlet 맵핑
- 싱클톤패턴
- SUB Query
- 사용자 데이터그램 프로토콜
- IP
- order by
- spring annotation
- 리스트 모달창
- 깃 명령어
- static메서드
- 다중 모달창
- static
- 템플릿
- 다운캐스팅
- GROUP BY
- 모달창 여러개
- react
- Java
- 객체협력
- Today
- Total
모든지 기록하자!
[Java] 인터페이스 요소 본문
인터페이스 상수
인터페이스는 추상 메서드로 이루어지므로 인스턴스를 생성할 수 없고 멤버 변수도 사용할 수 없다.
public interface Calc {
double PI = 3.14;
int ERROR = -99999999;
//인터페이스에는 상수와 추상메서드 선언
...(생략)
}
하지만 위처럼 선언해도 오류가 발생하지 않는 이유는 인터페이스에 선언한 변수를 컴파일하면
상수로 변환되기 때문이다. Calc 인터페이스에 선언한 변수 PI를 컴파일하면
public static final double PI = 3.14 , 상수 3.14로 변환된다. 그리고 int형 변수 ERROR 도 마찬가지로
public static final int ERROR = -99999999로 변환되어 상수로 취급된다.
디폴트 메서드와 정적 메서드
자바 7까지는 인터페이스에서 추상 메서드와 상수, 두 가지 요소만 선언해서 사용할 수 있었다.
어떤 인터페이스를 구현한 여러 클래스에서 사용할 메서드가 클래스마다 같은 기능을 제공하는 경우가
있다. 자바 7까지는 기능이 같아도 인터페이스에서 코드를 구현할 수 없기 때문에 추상 메서드를 선언하고
각 클래스마다 똑같이 그 기능을 반복해 구현해야 해서 번거롭다. 또 클래스를 생성하지 않아도 사용할 수 있는 메서드가 필요한 경우도 있는데, 인터페이스만으로는 메서드를 호출할 수가 없다.
자바 8부터는 인터페이스 활용성을 높이기 위해 디폴트 메서드와 정적 메서드 기능을 제공한다.
디폴트 메서드는 인터페이스에서 구현 코드까지 작성한 메서드이다. 정적 메서드는 인스턴스 생성과 상관없이 사용할 수 있는 메서드이다. 디폴트 메서드나 정적 메서드를 추가했다고 인터페이스가 인스턴스를
생성할 수 있는 것은 아니다.
디폴트 메서드
디폴트 메서드란 말 그대로 기본으로 제공되는 메서드다. 디폴트 메서드는 인터페이스에서 구현하지만
이후 인터페이스를 구현한 클래스가 생성되면 그 클래스에서 사용할 기본 기능이다. 디폴트 메서드를 선언할 때는 default 예약어를 사용한다.
public interface Calc {
...(생략)
default void description() {
System.out.println("정수 계산기를 구현합니다.");
}
}
디폴트 메서드는 일반 메서드와 똑같이 구현하고 메서드 자료형 앞에 default 예약어만 써주면 된다.
새로 구현한 description() 디폴트 메서드를 사용하려면 CompleteCalc 클래스를 생성해야 한다.
public class CalculatorTest {
public static void main(String[] args) {
int num1 = 10;
int num2 = 2;
CompleteCalc calc = new CompleteCalc(); // CompleteCalc 클래스 생성
System.out.println(calc.add(num1, num2));
System.out.println(calc.substract(num1, num2));
System.out.println(calc.times(num1, num2));
System.out.println(calc.divide(num1, num2));
calc.showInfo();
calc.description(); // 디폴트 메서드 호출
}
}
실행결과
디폴트 메서드는 인터페이스에 이미 구현되어 있으므로 인터페이스를 구현한 추상 클래스
Calculator나 추상 클래스를 상속받은 CompleteCalc 클래스에서 코드를 구현할 필요가 없다.
calc 인스턴스를 사용하여 description() 메서드를 호출했다.
디폴트 메서드 재정의하기
디폴트 메서드가 새로 생성한 클래스에서 원하는 기능과 맞지 않다면 하위 클래스에서 디폴트 메서드를
재정의할 수 있다. Calc 인터페이스를 구현하는 Calculator 클래스에서 재정의 할 수도 있고 Calculator 클래스를 상속받은 CompleteCalc 클래스에서 재정의할 수도 있다.
public class CompleteCalc extends Calculator{
...(생략)
@Override
public void description() { // 디폴트 메서드 description()을
//CompleteCalc 클래스에서 원하는 기능으로 재정의
super.description();
}
}
super.description()은 인터페이스에 선언한 메서드를 의미한다. 이 코드를 사용하지 않을 경우 지우고
새 코드를 작성하면 된다. CompleteCalc 클래스로 인스턴스를 생성해서 호출하면 재정의된 메서드가 호출된다.
정적 메서드
정적 메서드는 static 예약어를 사용하여 선언하며 클래스 생성과 무관하게 사용할 수 있다.
정적 메서드를 사용할 때는 인터페이스 이름으로 직접 참조하여 사용한다.
Calc인터페이스에 매개변수로 전달된 배열의 모든 요소 값을 더하는 정적 메서드 total()을 추가해보자
public interface Calc {
...(생략)
static int total(int[] arr) {
int total=0;
for(int i:arr) { // 인터페이스에 정적 메서드 total() 구현
total += i;
}
return total;
}
}
public class CalculatorTest {
public static void main(String[] args) {
int num1 = 10;
int num2 = 2;
CompleteCalc calc = new CompleteCalc(); // CompleteCalc 클래스 생성
System.out.println(calc.add(num1, num2));
System.out.println(calc.substract(num1, num2));
System.out.println(calc.times(num1, num2));
System.out.println(calc.divide(num1, num2));
calc.showInfo();
calc.description();
int[] arr = {1,2,3,4,5};
System.out.println(Calc.total(arr));
}
}
출력 결과
Calc.total(arr)처럼 인터페이스 이름으로 직접 참조하여 정적 메서드를 호출한다.
total() 메서드가 수행되면 배열의 모든 요소 값을 더한 결과 값을 반환해서 1부터 5까지 더한 값 15 출력
private 메서드
자바 9부터 인터페이스에 private 메서드를 구현할 수 있다. private 메서드는 인터페이스를 구현한 클래스에서 사용하거나 재정의할 수 없다. 기존에 구현된 코드를 변경하지 않고 인터페이스를 구현한 클래스에서 공통으로 사용하는 경우에 private 메서드로 구현하면 재사용성을 높일 수 있다. 또 클라이언트 프로그램에 제공할 기본 기능을 private 메서드로 구현하기도 한다.
private 메서드는 코드를 모두 구현해야 하므로 추상 메서드에 private 예약어를 사용할 수 없지만 static
예약어는 함께 사용 가능하다. private static 메서드는 정적 메서드에서 호출해서 사용한다.
public interface Calc {
double PI = 3.14;
int ERROR = -99999999;
//인터페이스에는 상수와 추상메서드 선언
int add(int num1, int num2);
int substract(int num1, int num2);
int times(int num1, int num2);
int divide(int num1, int num2);
default void description() {
System.out.println("정수 계산기를 구현합니다.");
myMethod(); // 디폴트 메서드에서 private 메서드 호출
}
static int total(int[] arr) {
int total=0;
for(int i:arr) {
total += i;
}
myStaticMethod(); // 정적 메서드에서 private static 메서드 호출
return total;
}
private void myMethod() { // private 메서드
System.out.println("private 메서드입니다.");
}
private static void myStaticMethod() { // private static 메서드
System.out.println("private static 메서드입니다.");
}
}
출력 결과
한 클래스가 여러 인터페이스를 구현하는 경우
한 클래스가 여러 클래스를 상속받으면 메서드 호출이 모호해지는 문제가 발생할 수 있다.
하지만 인터페이스는 한 클래스가 여러 인터페이스를 구현할 수 있다.
public interface Buy {
void buy();
}
}
public interface Sell {
void sell();
}
}
Buy 인터페이스에 추상 메서드 buy()가 선언되어 있고, Sell 인터페이스에 추상 메서드 sell()이 선언됐다.
Customer 클래스가 두 인터페이스를 구현하는 코드는 아래를 확인하자
public class Customer implements Buy, Sell{ // Customer 클래스는
// Buy와 Sell 인터페이스를 모두 구현했다.
@Override
public void sell() {
System.out.println("판매하기");
}
@Override
public void buy() {
System.out.println("구매하기");
}
}
인터페이스는 구현 코드나 멤버 변수를 가지지 않기 때문에 여러 개를 동시에 구현할 수 있다.
두 인터페이스에 이름이 같은 메서드가 선언되었다고 해도 구현은 클래스에서 이루어 지므로
어떤 메서드를 호출해야 하는지 모호하지 않다.
이렇게 두 인터페이스를 구현한 Customer 클래스는 Buy형이자 Sell형 이기도 하다.
테스트 프로그램을 확인해보자
public class CustomerTest {
public static void main(String[] args) {
Customer customer = new Customer();
Buy buyer = customer; // Customer 클래스 형인 customer를 Buy 인터페이스 형인
buyer.buy(); // buyer에 대입하여 형 변환. buyer는 Buy 인터페이스의 메서드만 호출가능
Sell seller = customer; // Customer 클래스 형인 customer를 Sell 인터페이스 형인
seller.sell(); // seller에 대입하여 형 변환. seller는 Sell 인터페이스의 메서드만 호출가능
if(seller instanceof Customer){
Customer customer2 = (Customer)seller;
customer2.buy(); // seller를 하위 클래스형인 Customer로 다시 형 변환
customer2.sell();
}
}
}
두 인터페이스의 디폴트 메서드가 중복되는 경우
정적 메서드는 인스턴스 생성과 상관없이 사용할 수 있다. Customer 클래스가 Buy, Sell 두 인터페이스를
구현하고 Buy 인터페이스와 Sell 인터페이스에 똑같은 pay() 정적 메서드가 있다고 생각해보자.
Buy.pay()와 Sell.pay()로 특정하여 호출할 수 있기 때문에 문제가 되지 않는다.
디폴트 메서드는 어떻게 될까? 디폴트 메서드는 인스턴스를 생성해야 호출할 수 있는 메서드이기 때문에
이름이 같은 디폴트 메서드가 두 인터페이스에 있으면 문제가 된다.
public interface Buy {
void buy();
default void order() {
System.out.println("구매주문");
}
}
public interface Sell {
void sell();
default void order() {
System.out.println("판매주문");
}
}
Buy와 Sell 인터페이스 모두 order() 디폴트 메서드를 가지고 있다. 이 상태에서 두 인터페이스를 모두 구현하면 Customer 클래스에는 오류 메시지가 나타난다. 'Duplicate default methods named order with the parameters () and () are inherited from the types Sell and Buy' 디폴트 메서드가 중복되었으니 두 인터페이스를 구현하는 Customer 클래스에서 재정의 하라는 뜻이다.
←
public class Customer implements Buy, Sell{
...(생략)
@Override
public void order() {
System.out.println("고객 판매 주문");
}
}
Cutomer 클래스에서 디폴트 메서드를 재정의하면 Customer 클래스를 생성하여 사용할 때
재정의된 메서드가 호출된다.
public class CustomerTest {
public static void main(String[] args) {
Customer customer = new Customer();
Buy buyer = customer;
buyer.buy();
buyer.order(); // 재정의된 메서드 호출
Sell seller = customer;
seller.sell();
seller.order(); // 재정의된 메서드 호출
if(seller instanceof Customer) { // seller에서 하위클래스 Customer로 형 변환
Customer customer2 = (Customer)seller;
customer2.buy();
customer2.sell();
customer2.order(); // 재정의된 메서드 호출
}
}
}
출력 결과
주의할 점은 customer가 Buy형으로 변환되고 buyer.order()를 호출하면 Buy에 구현한 디폴트 메서드가
아닌 Customer 클래스에 재정의한 메서드가 호출된다는 것이다.
인터페이스 상속하기
인터페이스 간에도 상속이 가능하다. 인터페이스끼리 상속은 구현 코드를 통해 기능을 상속하는 것이 아니기 때문에 형 상속(type inheritance)이라고 부른다. 클래스의 경우에는 하나의 클래스만 상속받을 수 있지만 인터페이스는 여러 개를 동시에 상속받을 수 있다. 한 인터페이스가 여러 인터페이스를 상속받으면
상속받은 인터페이스는 상위 인터페이스에 선언한 추상 메서드를 모두 가지게 된다.
MyInterface 인터페이스는 X와 Y 인터페이스를 상속받고 Myclass 클래스는 MyInterface 인터페이스를
실제로 사용할 수 있도록 구현한다. MyInterface 인터페이스는 두 인터페이스를 상속받고 자신이
추상 메서드를 1개 가지고 있으므로 상속받은 후 추상 메서드를 총 3개 가지게 된다.
따라서 MyClass 클래스가 구현해야 할 추상 메서드 개수는 총 3개다.
public interface X {
void x(); // 추상 메서드 x()선언
}
public interface Y {
void Y(); // 추상 메서드 y()선언
}
public interface MyInterface extends X,Y {
// 인터페이스 여러 개를 상속 받을 수 있다.
void myMethod();
}
public class MyClass implements MyInterface{
@Override
public void x() { // X 인터페이스에서 상속받은
System.out.println("x()"); // x() 메서드 구현
}
@Override
public void Y() { // Y 인터페이스에서 상속받은
System.out.println("y()"); // y() 메서드 구현
}
@Override
public void myMethod() { // MyInterface 인터페이스의
System.out.println("myMethod()"); // myMethod() 메서드 구현
}
MyInterface 인터페이스에는 myMethod() 메서드만 선언되어 있지만 X 인터페이스와 Y 인터페이스를
상속받았으므로 MyClass 클래스에서는 x(), y() 메서드까지 구현해야 한다.
테스트 프로그램을 실행해보자
public class MyClassTest {
public static void main(String[] args) {
MyClass mClass = new MyClass();
X xClass = mClass; // 상위 인터페이스 X형으로 대입하면
xClass.x(); // X에 선언한 메서드만 호출 가능
Y yClass = mClass; // 상위 인터페이스 Y형으로 대입하면
yClass.Y(); // Y에 선언한 메서드만 호출 가능
MyInterface iClass = mClass;
iClass.myMethod(); // 구현한 인터페이스형 변수에 대입하면
iClass.x(); // 인터페이스가 상속한 모든 메서드 호출 가능
iClass.Y();
}
}
출력 결과
인터페이스 구현과 클래스 상속 함께 쓰기
BookShelf(책장) 클래스는 책을 넣은 순서대로 꺼내 볼 수 있도록 만들자 그전에 더 큰 개념인
Shelf(선반) 클래스를 만들어 보자
public class Shelf {
protected ArrayList<String> shelf; // 자료를 순서대로 저장할 ArrayList 선언
public Shelf() {
shelf = new ArrayList<String>(); // 디폴트 생성자로 Shelf 클래스를 생성하면
} // ArrayList도 생성된다.
public ArrayList<String> getShelf(){
return shelf; // 저장되어 있는 배열 Shelf 반환
}
public int getCount() {
return shelf.size(); // 저장된 요소 개수를 반환한다.
}
}
먼저 들어온 자료를 먼저 꺼내는 기능을 가진 Queue 인터페이스를 정의해 보자
public interface Queue {
void enQueue(String title); // 배열의 맨 마지막에 추가
String deQueue(); // 배열의 맨 처음 항목 반환
int getSize(); // 현재 Queue에 있는 개수 반환
}
BookShelf 클래스 구현하기
public class BookShelf extends Shelf implements Queue {
@Override
public void enQueue(String title) {
shelf.add(title); // 배열에 요소 추가
}
@Override
public String deQueue() { // 맨 처음 요소를 배열에서 삭제하고 반환
return shelf.remove(0);
}
@Override
public int getSize() { // 배열 요소 개수 반환
return getCount();
}
}
BookShelf 테스트 하기
public class BookShelfTest {
public static void main(String[] args) {
Queue shelfQueue = new BookShelf();
shelfQueue.enQueue("슬램덩크1권"); // 순서대로 요소 추가
shelfQueue.enQueue("슬램덩크2권");
shelfQueue.enQueue("슬램덩크3권");
System.out.println(shelfQueue.deQueue()); // 입력 순서대로 요소를 꺼내서 출력한다.
System.out.println(shelfQueue.deQueue());
System.out.println(shelfQueue.deQueue());
}
}
출력 결과
'Java' 카테고리의 다른 글
[Java] 컬렉션 프레임워크 (collection framework) (0) | 2021.05.21 |
---|---|
[Java] 제네릭 (generic) (0) | 2021.05.20 |
[Java] 인터페이스 ( interface) ( 예제: 상담원 배분) (0) | 2021.05.18 |
[Java] 템플릿(template) 메서드 , final 예약어 (예제 : Player) (0) | 2021.05.18 |
[Java] 추상 클래스 ( abstract class ) (예제 : AI Car) (1) | 2021.05.18 |