6장. 상속과 인터페이스
Section 1. 클래스의 상속 |
|||||||||||||||||||||||||||||||||||||
264 |
클래스의 상속(inheritance) 기존의 클래스를 가져다가 확장해서 새로운 클래스를 만드는 기술 기존 클래스 새로만들 클래스
위와 같은 경우 은행계좌 클래스를 상속받아 새로 추가할 부분만 추가해서 새로운 클래스를 생성한다. < 기존의 Account 클래스 > class Account { String accountNo; String ownerName; int balance;
int withdraw(int amount)throws Exception{ if(balance < amount) throw new Exception("잔액이 부족합니다."); balance -= amount; return amount; } }
< Account를 상속받아 만든 CheckingAccount >
public class CheckingAccount extends Account{ String cardNo; int pay(String cardNo, int amount) throws Exception{ if(!cardNo.equals(this.cardNo) || (balance < amount)) throw new Exception("지불이 불가능 합니다."); return withdraw(amount); } }
- 다른 클래스를 상속받을 때에는 클래스의 이름과 메소드 본체 사이에 extends절을 넣으면 된다. - 자바에서 다른 클래스에게 상속해주는 클래스를 SuperClass라고 하고 상속을 받는 클래스를 SubClass라고 한다. Account는 CheckingAccount의 슈퍼클래스 이고, CheckingAccount는 Account의 서브 클래스. - 상속받은 클래스는 상속한 클래스의 필드와 메소드를 마치 이 클래스 안에 선언되어 있는 것처럼 사용. - 다른 클래스를 상속해서 만든 클래스를 다시 상속해서 또 다른 클래스를 선언하는 것도 가능하다. |
||||||||||||||||||||||||||||||||||||
268 |
다른 클래스를 상속하는 클래스의 사용방법 일반적인 클래스의 사용방법과 동일하다. 다만 슈퍼클래스로부터 상속받은 필드와 메소드들도 사용할 수 있다는 점이 다를 뿐이다. public class InheritanceExample1 { public static void main(String[] args) { CheckingAccount obj = new CheckingAccount(); obj.accountNo = "111-22-333333"; obj.ownerName = "홍길동 "; obj.cardNo = "5555-6666-7777-8888"; obj.deposit(100000); try { int paidAmount = obj.pay("5555-6666-7777-8888", 47000); System.out.println("지불액 : "+paidAmount); System.out.println("잔액: " + obj.balance); } catch(Exception e){ String msg = e.getMessage(); System.out.println(msg); } }
}
<결과> 지불액 : 47000 잔액: 53000 |
||||||||||||||||||||||||||||||||||||
270 |
생성자가 추가된 CheckingAccount(서브클래스) 참고로 현재는 ChekcingAccount의 슈퍼클래스인 Account의 클래스에 생성자가 없다. public class CheckingAccount extends Account{ String cardNo; CheckingAccount (String accountNo, String ownerName, int balance, String cardNo){ this.accountNo = accountNo; this.ownerName = ownerName; this.balance = balance; this.cardNo = cardNo; } int pay(String cardNo, int amount) throws Exception{ if(!cardNo.equals(this.cardNo) || (balance < amount)) throw new Exception("지불이 불가능 합니다."); return withdraw(amount); } }
|
||||||||||||||||||||||||||||||||||||
271 |
생성자가 있는 슈퍼클래스를 상속하는 방법 ChekcingAccount의 슈퍼클래스인 Account의 클래스에 생성자를 추가하면 어떻게 될까? class Account { String accountNo; String ownerName; int balance;
Account(String accountNo, String ownerName, int balance){ this.accountNo = accountNo; this.ownerName = ownerName; this.balance = balance; }
void deposit(int amount){ balance += amount; }
int withdraw(int amount)throws Exception{ if(balance < amount) throw new Exception("잔액이 부족합니다."); balance -= amount; return amount; } }
이렇게 하고서 서브클래스인 CheckingAccount.java를 컴파일 해보면 다음과 같인 error가 발생한다.
C:\work>javac CheckingAccount.java CheckingAccount.java:4: cannot find symbol symbol : constructor Account() location: class Account int balance, String cardNo){ ^ 1 error
C:\work>
메시지 내용은 Account()라는 생성자를 찾을 수 없다고 말하고 있다. 그리고 이 컴파일 에러가 checkingAccount.java의 4행 마지막 부분, 그러니까 CheckingAccount 클래스의 생성자 본체가 시작하는 곳에서 발생했다고 나오고 있다. 그 이유는 자바컴파일러가 컴파일 할 때 생성자의 첫 번째 명령문이 슈퍼클래스의 생성자 호출문이 아니면 자동으로 슈퍼클래스의 no-arg constructor 호출문을 그 위치에 추가하기 때문이다. 그런데 위 예에서는 Account 클래스에 no-arg constructor 가 없기 때문에 컴파일 에러가 발생한 것이다.
해결 방법은 두가지이다. 1. 한가지는 슈퍼클래스인 Account 클래스에 no-arg constructor를 추가하는 것이고 2. 다른 하나는 CheckingAccount 클래스의 생성자 안에 슈퍼클래스인 Accout클래스의 생성자 호출문을 명시적으로 써넣는 것이다. 자바 컴파일러는 생성자 안에 슈퍼클래스의 생성자 호출문이 있으면 no-arg constructor 호출문을 추가하지 않기 때문이다. 객체지향 프로그래밍에서는 슈퍼클래스의 소스코드를 건드리지 않고 상속받는 것을 원칙으로 하기 때문에 후자가 더 낳은 해결책이다.
두번째 방법으로 해결하기 super(accountNo, ownername, balance); 서브클래스의 생성자 안에서 슈퍼클래스의 생성자를 호출할 때는 슈퍼클래스의 이름 대신에 super키워드를 써야한다. public class CheckingAccount extends Account{ String cardNo; CheckingAccount (String accountNo, String ownerName, int balance, String cardNo){ super(accountNo, ownerName, balance); this.accountNo = accountNo; this.ownerName = ownerName; this.balance = balance; this.cardNo = cardNo; } int pay(String cardNo, int amount) throws Exception{ if(!cardNo.equals(this.cardNo) || (balance < amount)) throw new Exception("지불이 불가능 합니다."); return withdraw(amount); } } 단, 슈퍼클래스의 생성자 호출문은 반드시 생성자의 첫 번재 명령문이어야 함을 기억해야 한다. |
||||||||||||||||||||||||||||||||||||
273 |
메소드 오버라이딩(method overriding) 슈퍼클래스의 메소드와 똑 같은 시그니쳐의 메소드를 만들어서 슈퍼클래스의 메소드를 무시하도록 만드는 것을 메소드 오버라이딩 이라고 한다.
- 상속을 통해서 기존의 기능을 바꾸는 일도 할 수 있다. - 다음과 같이 마이너스 통장을 만들경우 Account 클래스의 필드와 공통점이 많으므로 상속하는 것이 좋다. 기존 클래스 새로만들 클래스
위와 같이 필드는 마이너스 한도만을 추가하고 메소드는 인출한다 라는 메소드의 기능이 다르게 수행되도록 하면 된다. 방법은, 슈퍼클래스와 똑 같은 시그너쳐를 갖는 widthdraw 메소드를 서브 클래스에 선언하기만 하면 된다. 그러면 슈퍼클래스의 widthdraw 메소드는 무시되고 서브클래스의 widthdraw 메소드만 사용된다. cf) 메소드 오버라이딩은 시그니쳐가 같은 메소드에 대해서만 일어난다. class CreditLineAccount extends Account{ int creditLine; CreditLineAccount(String accountNo, String ownerNo, int balance, int creditLine){ super(accountNo, ownerNo, balance); this.creditLine = creditLine; }
int widthdraw(int amount) throws Exception{ if((balance + creditLine) < amount) throw new Exception("인출이 불가능합니다."); balance -= amount; return amount;
}
}
< 마이너스 통장 클래스를 이용하는 프로그램 > public class InheritanceExample3 { public static void main(String[] args) { CreditLineAccount obj = new CreditLineAccount( "0000-11-111111", "김선달", 10000, 2000000); try{ int amount = obj.widthdraw(50000); System.out.println("인출액:"+amount); System.out.println("잔 액:"+obj.balance); System.out.println("마이너스 한도:"+obj.creditLine); } catch(Exception e){ System.out.println(e.getMessage()); } } }
<결과> 인출액:50000 잔 액:-40000 마이너스 한도:2000000
|
||||||||||||||||||||||||||||||||||||
276 |
오버라이드하는 메소드 안에서 오버라이드된 메소드 호출하기
Account클래스를 상속받아서 필드의 경우 ‘누적 포인트’라는 필드는 추가만 하면 된다 하지만 ‘예금한다’ 메소드에서는 예금액의 0.1%씩 적립하도록 하고싶다. 기존 클래스 새로만들 클래스
- 우선 오버라이드 해서 위 문제를 해결할 수 있다. class BonusPointAccount extends Account { int bonusPoint; BonusPointAccount(String accountNo, String ownerName, int balance, int bonusPoint){ super(accountNo, ownerName, balance); this.bonusPoint = bonusPoint; } void deposit(int amount){ balance += amount; bonusPoint +=(int)(amount * 0.001); } }
- 이렇게 클래스를 선언해 놓고 보니 deposit메소드 안에서 하는 일은 Account클래스의 deposit메소드 안에서 하는 일을 확장한 것에 불과하다. 0.1%의 포인트를 누적 하는 부분이 추가 되었을 뿐이다. - 이 경우 똑 같은 코드를 다시 작성하는 것 보다 서브클래스의 메소드에서 슈퍼클래스의 메소드를 호출하는 편이 낫다. 대신 super.deposit(amount); 이런식으로 super 키워드를 사용해야 한다. class BonusPointAccount extends Account { int bonusPoint; BonusPointAccount(String accountNo, String ownerName, int balance, int bonusPoint){ super(accountNo, ownerName, balance); this.bonusPoint = bonusPoint; } void deposit(int amount){ super.deposit(amount); bonusPoint +=(int)(amount * 0.001); } } |
||||||||||||||||||||||||||||||||||||
279 |
상속을 금지하는 final 키워드 - 자기가 만든 클래스를 다른 프로그래머가 상속받아서 사용하지 못하도록 막야야 할 경우 사용. final 키워드를 이용한 클래스의 상속금지 클래스를 선언할 때 class 키워드 앞에 final 이라는 키워드를 쓰면 다른 클래스가 그 클래스를 상속할 수 없게 된다. 이렇게 final 키워드를 붙여서 선언한 클래스를 final 클래스 라고 한다.
Account 클래스에 final 키워드 추가 final class Account { String accountNo; String ownerName; int balance;
Account(String accountNo, String ownerName, int balance){ this.accountNo = accountNo; this.ownerName = ownerName; this.balance = balance; } ............ ............ ............ ............ }
BonusPointAccount 클래스 등의 Accout클래스를 상속받은 클래스들에서 에러가 발생할 것이다.
final 키워드를 이용한 메소드 오버라이딩 금지 메소드를 선언할 때 맨 앞에 final 키워드를 써주면 된다. class Account { ............ ............
final int withdraw(int amount)throws Exception{ if(balance < amount) throw new Exception("잔액이 부족합니다."); balance -= amount; return amount; } }
앞서 작성한 CreditLineAccount 클래스에서 widthdraw 메소드를 오버라이딩 하였는데, 함께 컴파일 해보면 에러가 발생한다. |
||||||||||||||||||||||||||||||||||||
281 |
인스턴스화를 금지하는 abstract 키워드 클래스를 가지고 객체를 만드는 일을 인스턴스화(instantiation)이라고 한다. 어떤 경우에는 클래스의 인스턴스화를 막아야 할 경우도 있다. 이 때 abstract 키어드를 사용하면 된다.
abstract 키워드를 이용한 클래스의 인스턴스화 금지 예를 들어 Account, CheckingAccount, CreditLineAccount, BonusPointAccount를 사용하는 은행업무 프로그램을 개발하다가 더 이상 보통계금계좌(Account)를 더 이상 개설하지 않기로 결정했다. 그러니까 더 이상 이 은행계좌클래스의 객체를 더 이상 생성하면 안 된다. 이 경우 Accout 클래스는 다른 클래스의 슈퍼 클래스 이므로 인스턴스화는 막더라도 아주 없애면 안 된다. 이 경우엔 다음과 같이 Accout 클래스의 선언 제일 앞에 abstract 키워드를 사용하면 된다. abstract class Account { String accountNo; String ownerName; int balance;
Account(String accountNo, String ownerName, int balance){ this.accountNo = accountNo; this.ownerName = ownerName; this.balance = balance; } ............ ............ ............ ............ }
abstract 클래스를 테스트 하기 public class InheritanceExample5 { public static void main(String[] args) { Account obj = new Account("111-111-1111", "임꺽정", 0); }
} 이렇게 하면 에러가 발생한다. Account는 abstract 키워드로 더 이상 객체를 생성할 수 없게 했기 때문이다.
abstract 키워드를 붙인 클래스를 추상 클래스(abstract class)라고 한다.
|
||||||||||||||||||||||||||||||||||||
283 |
메소드 본체가 없는 추상 메소드 인스턴스화를 막기 위해 추상클래스로 만드는 경우도 있지만 때로는 처음 개발할 때부터 추상 클래스로 선언해야 할 경우도 있다.
위의 경우 공통점이 많고, 나중에 다른 메체로 메시지를 보내는 클래스를 추가할 필요도 있기 때문에, 공통점을 뽑아서 슈퍼클래스를 만들어 두는 편이 좋을 것 같다.
하지만 ‘메시지를 송신한다’ 메소드는 매체의 종류마다 완전히 다른 로직으로 작성해댜 한다. 이럴 때는 메소드 본체가 없는 메소드를 선언하면된다. 이런 메소드를 추상 메소드(abstract method)라고 한다. abstract void sendMessage(String recipient); 리턴 타입 앞에 abstract 키우드를 쓰고, 메소드 본체가 오는 자리에 ‘;’을 써야한다. 추상 메소드를 선언할 때에의 주의할 점은 클래스 자체도 추상 클래스로 선언 해야 한다는 점이다. <추상메소드가 포함된 추상클래스이면서 슈퍼클래스가 될 MessageSender.java> abstract class MessageSender { String title; String senderName; MessageSender(String title, String senderName){ this.title = title; this.senderName = senderName; } abstract void sendMessage(String recipient); }
EMailSender.java 이메일을 처리 할 클래스 class EMailSender extends MessageSender{ String senderAddr; String emailBody; EMailSender(String title, String senderName, String senderAddr, String emailBody){ super(title, senderName); this.senderAddr = senderAddr; this.emailBody = emailBody; }
void sendMessage(String recipient){ System.out.println("---------------------"); System.out.println("제목:" +title); System.out.println("보내는 사람 : " + senderName + " " + senderAddr); System.out.println("받는 사람 : " + recipient); System.out.println("내용 : " + emailBody); }
}
SSMSender.java 전화 메시지를 처리할 클래스 public class SSMSender extends MessageSender{ String returnPhoneNo; String message; SSMSender(String title, String senderName, String returnPhoneNo, String message){ super(title, senderName); this.returnPhoneNo = returnPhoneNo; this.message = message; }
void sendMessage(String recipient){ System.out.println("---------------------"); System.out.println("제목:" +title); System.out.println("보내는 사람 : " + senderName); System.out.println("전화번호 : " + recipient); System.out.println("회신 전화번호:" +returnPhoneNo); System.out.println("내용 : " + message); } }
위를 실행해보는 클래스 InheritanceExample6.java public class InheritanceExample6 { public static void main(String[] args) { EMailSender obj1 = new EMailSender("생일축하합니다.", "고객센터" , "admin@dukeeshop.co.kr", "10%할인 쿠폰이 발행되었습니다."); SSMSender obj2 = new SSMSender("생일축하합니다.", "고객센터" , "02-7264-2462", "10%할인 쿠폰이 발행되었습니다.");
obj1.sendMessage("hatman@yeyeye.com"); obj1.sendMessage("stickman@hahaha.com"); obj2.sendMessage("010-111-2462"); }
}
<결과> --------------------- 제목:생일축하합니다. 보내는 사람 : 고객센터 admin@dukeeshop.co.kr 받는 사람 : hatman@yeyeye.com 내용 : 10%할인 쿠폰이 발행되었습니다. --------------------- 제목:생일축하합니다. 보내는 사람 : 고객센터 admin@dukeeshop.co.kr 받는 사람 : stickman@hahaha.com 내용 : 10%할인 쿠폰이 발행되었습니다. --------------------- 제목:생일축하합니다. 보내는 사람 : 고객센터 전화번호 : 010-111-2462 회신 전화번호:02-7264-2462 내용 : 10%할인 쿠폰이 발행되었습니다.
|
||||||||||||||||||||||||||||||||||||
287 |
추상메소드를 선언하는 목적 MessageSender 클래스의 sendMessage메소드는 본체가 없기 때문에 서브클래스에 상속해줄 로직도 없고, MessageSender 객체를 만들 수 없기 때문에 직접 호출 할 수도 없다. 그런데 추상메소드를 선언했다 그 이유는? --> 첫번째 목적은 서브클래스에서 이 메소드를 반드시 구현하도록 만들기 위해서 이다. MessageSender 클래스를 상속받은 클래스들은 반드시 sendMessage메소드를 구현 해야 한다. 다른 이유는 다형성에 관련된 이유인데 이 부분은 바로 후에 설명 예정. |
||||||||||||||||||||||||||||||||||||
288 |
클래스 변수의 다형성(polymorphism) 클래스는 객체를 생성하는 도구로도 사용되지만 객체의 타입으로도 사용된다. 그런데 자바에서는 클래스 변수에 그 클래스로 만든 객체만 대입할 수 있는 게 아니라 그 클래스의 서브클래스 객체도 대입할 수 있다. 이렇게 하나의 변수에 여러 종류의 데이터를 대입할 수 있는 성질을 변수의 다형성 이라고 한다. 클래스 변수의 다형성 Account 클래스 타입으로 선언한 변수는 다형성을 갖기 때문에 Account 객체뿐만 아니라 서브 클래스인 CheckingAccount, CreditLineAccount, BonusPointAccount 의 객체들도 대입할 수 있다.
public class InheritanceExample7 { public static void main(String[] args) { Account obj1 = new Account("111-222-2222", " 임꺽정", 10000); CheckingAccount obj2 = new CheckingAccount( "444-153-63523", "홍길동 ", 20000, "555-66666-32742-21"); CreditLineAccount obj3 = new CreditLineAccount( "777-88-99999", "rlatjsekf", 30000, 2000000); BonusPointAccount obj4 = new BonusPointAccount( "00-00-000000", "김미영", 0, 0);
printAccountInfo(obj1); printAccountInfo(obj2); printAccountInfo(obj3); printAccountInfo(obj4); } static void printAccountInfo(Account obj){ System.out.println("계좌번호"+ obj.accountNo); System.out.println("예금주 이름:" + obj.ownerName); System.out.println("잔액 : " + obj.balance); System.out.println(); }
} <결과> 계좌번호111-222-2222 예금주 이름: 임꺽정 잔액 : 10000
계좌번호444-153-63523 예금주 이름:홍길동 잔액 : 20000
계좌번호777-88-99999 예금주 이름:rlatjsekf 잔액 : 30000
계좌번호00-00-000000 예금주 이름:김미영 잔액 : 0
다형성과 메소드 오버라이딩 앞서 작성한 MessageSender 클래스 타입 변수의 다형성을 이용하는 프로그램 public class InheritanceExample8 { public static void main(String[] args) { EMailSender obj1 = new EMailSender("생일축하합니다.", "고객센터" , "admin@dukeeshop.co.kr", "10%할인 쿠폰이 발행되었습니다."); SSMSender obj2 = new SSMSender("생일축하합니다.", "고객센터" , "02-7264-2462", "10%할인 쿠폰이 발행되었습니다.");
send(obj1, "hatman@yeyeye.com"); send(obj1, "stickman@hahaha.com"); send(obj2, "010-111-2462"); } static void send(MessageSender obj, String recipient){ obj.sendMessage(recipient); }
} <결과> --------------------- 제목:생일축하합니다. 보내는 사람 : 고객센터 admin@dukeeshop.co.kr 받는 사람 : hatman@yeyeye.com 내용 : 10%할인 쿠폰이 발행되었습니다. --------------------- 제목:생일축하합니다. 보내는 사람 : 고객센터 admin@dukeeshop.co.kr 받는 사람 : stickman@hahaha.com 내용 : 10%할인 쿠폰이 발행되었습니다. --------------------- 제목:생일축하합니다. 보내는 사람 : 고객센터 전화번호 : 010-111-2462 회신 전화번호:02-7264-2462 내용 : 10%할인 쿠폰이 발행되었습니다.
send 메소드 안에서는 obj가 어느 클래스의 객체이든 상관없이 무조건 sendMessage메소드를 호출하고 있다. EMailsender 객체와 SSMSender 객체를 MessageSender 변수에 대입해도 객체의 타입이 바뀌는 것이 아니기 때문에 이런 결과가 나온것이다.
클래스 변수의 다형성을 활용할 때 주의할 점 자바 컴파일러는 객체가 아니라 변수의 타입만 가지고 그 메소드가 있는지 없는지를 체크 한다. 그렇기 때문에 MessageSender 클래스에서 sendMessage메소드를 빼버리면 프로그램을 컴파일 할 때 에러가 발생할 것이다. 이것이 바로 추상 메소드 선언의 두 번째 목적이다. 슈퍼클래스의 메소드를 호출하는 척 하면서 컴파일러의 체크를 무사 통과하고, 프로그램이 실행될 때는 서브클래스의 메소드가 호출 되도록 하느 것, 그것이 추상메소드의 또 다른 사용 목적인 것이다. |
||||||||||||||||||||||||||||||||||||
Section 2. 인터페이스 |
|||||||||||||||||||||||||||||||||||||
292 |
인터페이스 자바에서는 클래스의 다중 상속을 허용하지 않는다.( 서브클래스 선언 시 여러 개의 슈퍼클래스를 지정할 수 없다)하지만 때로는 이미 다른 클래스를 상속 받은 서브 클래스들의 공통점을 추출해서 또 다른 슈퍼 클래스를 선언하고 싶을 경우가 있다.
공통점만 추출해서 인터페이스로 만들면 된다. - 단, 인터페이스는 클래스들의 공통 기능만 표현할 수 있고, 공통 데이터는 표현할 수 없다는 제약 있다.
클래스와 인터페이스의 관계는 클래스 쪽에서 implements라는 자바 키워드와 인터페이스 이름을 쓰는 것으로 맺어진다. 그래서 이 클래스와 인터페이스의 관계를 표현할 때는 상속이라는 용어 대신 구현(implementation)이라는 용어를 사용한다. ‘SeperateVolume(단행본)클래스는 Lendable(대출가능)인터페이스를 구현한다’ 라고 표현할 수 있다. |
||||||||||||||||||||||||||||||||||||
295 |
인터페이스 선언방법 - interface라는 키워드와 abstract로 메소드를 선언해야 한다.(abstract 생략가능) -자바의 식별자 명명 관례에 따르면 인터페이스의 이름은 대문자로 시작하는 명사나 형용사로 하는 것이 좋고, 메소드는 소문자로 시작하는 동사로 정하는 것이 좋다. 인터페이스에 속하는 메소드는 무조건 추상 메소드로 선언해야 한다. interface Lendable { abstract void checkOut(String aborrower, String date); abstract void checkIn();
} - 인터페이스의 메소드는 무조건 추상 메소드 이기 때문에 선언에서 abstract 키워드를 쓰지 않아도 됨 interface Lendable { void checkOut(String aborrower, String date); void checkIn();
} 인터페이스를 구현하는 클래스에서는 implements 절을 써야 한다. 그리고 또 해당 메소드의 리턴형 앞에는 public 키워드를 반드시 써야 한다. – 이유는 추후에 설명. < 단행본 클래스 > class SeparateVolume implements Lendable{ String requestNo; String bookTitle; String writer; String borrower; String checkOutDate; byte state;
SeparateVolume(String requestNo, String bookTitle, String writer){ this.requestNo = requestNo; this.bookTitle = bookTitle; this.writer = writer; } public void checkOut(String borrower, String date){ if(state !=0) return ; this.borrower = borrower; this.checkOutDate= date; this.state = 1; System.out.println("*" +bookTitle + "이(가) 대출되었습니다."); System.out.println("대출인:" + borrower); System.out.println("대출일:" + date + "\n");
} public void checkIn(){ this.borrower = null; this.checkOutDate = null; this.state = 0; System.out.println("*"+ bookTitle + " 이(가) 반납되었습니다."); } } 부록 CD 클래스 “부록 CD 클래스”는 “CD 클래스”를 상속하면서 동시에 lendable 인터페이스를 구현 해야한다. 먼저 extends를 쓰고 implements 저을 쓰는 것이 순서이다. 먼저 “CD 클래스” public class CDInfo { String registerNo; String title; CDInfo (String registerNo, String title){ this.registerNo = registerNo; this.title = title; } } “부록 CD 클래스” public class AppCDInfo extends CDInfo implements Lendable{ String borrower; String checkOutDate; byte state; AppCDInfo(String registerNo, String title){ super(registerNo, title); } public void checkOut(String borrower, String date){ if(state !=0) return; this.borrower = borrower; this.checkOutDate = date; this.state = 1; System.out.println("*" + title + "CD가 대출되었습니다."); System.out.println("대출인:" +borrower); System.out.println("대출일:" + date +"\n"); } public void checkIn(){ this.borrower = null; this.checkOutDate = null; this.state = 0; System.out.println("*"+title+"CD가 반납되었습니다."); } }
인터페이스를 가지고 할 수 없는 일 인터페이스를 이용하여 객체를 만들 수 없다. 그래서 Leandable 인터페이스를 다음과 같이 사용하면 안됨 obj = new Landable( );
인터페이스를 가지고 할 수 있는 일 추상메소드만 가질 수 있기 때문에 인터페이스를 구현하는 클래스에게 메소드의 로직을 상속해 줄 수는 없다. 하지만 추상메소드를 이용하여 그 인터페이스를 구현하는 클래스의 선언 방법을 제한 할 수 있다. |
||||||||||||||||||||||||||||||||||||
301 |
인터페이스 변수의 다형성 인터페이스는 변수의 선언에 사용될 수 있다. 예를 들어 Lendable 인터페이스 타입의 변수는 다음과 같이 선언할 수 있다. Lendable obj; 인터페이스 변수에는 그 인터페이스를 구현(implement)하는 클래스의 객체라면 어떤 객체든지 다 대입할 수 있다. 그러니까 위와 같이 선언된 obj 변수에는 Lendable 인터페이스를 구현하는 SeparateVolume 클래스와 AppCDInfo 클래스의 객체를 모두 대입할 수 있다는 것이다. obj = new SeparateVolume(“863ㅁ774개”, “개미”, “베르베르”); obj = new AppCDInfo(“2006-7001”, “Redhat Fedora”); 이렇게 한 변수에 여러 종류의 데이터를 대입할 수 있는 성질을 변수의 다형성이라고 한다. 그러니까 인터페이스는 다형성을 갖는다고 말할 수 있다.
인터페이스 변수의 다형성을 이용해서 단행본과 부록 CD 를 한꺼번에 대출하는 메소드 작성 class InterfaceExample2 { public static void main(String[] args) { Lendable arr[] = new Lendable[3]; arr[0] = new SeparateVolume("883ㅇ326ㅍ2", "푸코의 진자", "에코"); arr[1] = new SeparateVolume("609.2ㄱ428ㅅ", "서양미술사", "곰브리치"); arr[2] = new AppCDInfo("2002-1742", "XML을 위한 자바 프로그래밍");
checkOutAll(arr, "윤지해", "20060315"); } static void checkOutAll(Lendable arr[], String borrower, String date){ for(int cnt=0; cnt< arr.length; cnt++){ arr[cnt].checkOut(borrower, date); } } } < 결과 > *푸코의 진자이(가) 대출되었습니다. 대출인:윤지해 대출일:20060315
*서양미술사이(가) 대출되었습니다. 대출인:윤지해 대출일:20060315
*XML을 위한 자바 프로그래밍CD가 대출되었습니다. 대출인:윤지해 대출일:20060315
|
||||||||||||||||||||||||||||||||||||
302 |
인터페이스의 상수 필드 인터페이스에는 인스턴스 필드는 선언할 수 없지만, 상수 필드만큼은 선언할 수 있다. final static int MAXIMUM = 100; 그런데 인터페이스에서는 final static 키워드를 쓰지 않아도 자바 컴파일러가 컴파일 할 때 자동으로 이 두 키워드를 추가해 주므로 쓰지 않아도 된다. 사용 예로는 앞서 작성한 단행본 클래스와 부록 CD 클래스에서 대출 상태 표현을 위해 0과 1을 사용하였는데, 이 상수들은 Lendable 인터페이스의 checkout, checkIn 메소드와 밀접한 관련이 있으므로 Lendable 인터페이스 안에 상수 필드로 선언해 두는 것이 좋다.
interface Lendable { final static byte STATE_BORROWED = 1; final static byte STATE_NORMAL = 0; void checkOut(String borrower, String date); void checkIn(); } cf) 인터페이스에서는 정적 초기화 블록을 사용할 수 없으므로 반드시 선언문에서 초기 값을 대입해야한다. – final로 선언된 필드는 객체가 생성되는 도중에 반드시 초기 값을 대입해야 한다.
인터페이스에 상수 정의 후에 SeparateVolume을 수정해본다. class SeparateVolume implements Lendable{ String requestNo; String bookTitle; String writer; String borrower; String checkOutDate; byte state;
SeparateVolume(String requestNo, String bookTitle, String writer){ this.requestNo = requestNo; this.bookTitle = bookTitle; this.writer = writer; } public void checkOut(String borrower, String date){ if(state != STATE_BORROWED) return ; this.borrower = borrower; this.checkOutDate= date; this.state = STATE_BORROWED; System.out.println("*" +bookTitle + "이(가) 대출되었습니다."); System.out.println("대출인:" + borrower); System.out.println("대출일:" + date + "\n");
} public void checkIn(){ this.borrower = null; this.checkOutDate = null; this.state = STATE_NORMAL; System.out.println("*"+ bookTitle + " 이(가) 반납되었습니다."); } } - 인터페이스로부터 상속받은 상수는 마치 클래스 내부에 선언된 상수처럼 자유롭게 사용할 수 있다. 하지만 인터페이스를 구현하지 않는 클래스에서 이 상수 필드를 사용하기 위해서는 필드 이름 앞에 인터페이스 이름을 써주어야 한다. Lendable.STATE_NORMAL Lendable.STATE_BORROWED |
||||||||||||||||||||||||||||||||||||
306 |
익셉션을 발생하는 추상 메소드 – 인터페이스와 인터페이스를 구현하는 클래스의 메소드 두 곳 모두에서 throws 절을 써야한다. interface Lendable { final static byte STATE_BORROWED = 1; final static byte STATE_NORMAL = 0; void checkOut(String borrower, String date) throws Exception; void checkIn(); }
class SeparateVolume implements Lendable{ .......... .......... .......... public void checkOut(String borrower, String date) throws Exception{ if(state != STATE_BORROWED) throw new Exception("*대출불가:"+bookTitle); this.borrower = borrower; this.checkOutDate= date; this.state = STATE_BORROWED; System.out.println("*" +bookTitle + "이(가) 대출되었습니다."); System.out.println("대출인:" + borrower); System.out.println("대출일:" + date + "\n");
} public void checkIn(){ this.borrower = null; this.checkOutDate = null; this.state = STATE_NORMAL; System.out.println("*"+ bookTitle + " 이(가) 반납되었습니다."); } } |
||||||||||||||||||||||||||||||||||||
308 |
인터페이스의 상속 인터페이스도 상속 할 수 있다. 다른 인터페이스를 상속하는 인터페이스를 선언할 때는 인터페이스 이름과 본체 사이에 extends 절을 써주어야 한다. 위치 이동 인터페이스를 상속하는 변환 인터페이스 public interface Transformable extends Movable { void resize(int width, int height); }
Transformable의 슈퍼 인터페이스 interface Movable { abstract void moveTo(int x, int y); abstract void moveBy(int xOffset, int yOffset); } Transformable 인터페이스를 구현하는 클래스 -Transformable에 있는 메소드 뿐만 아니라 Movable 에 있는 메소드도 구현 해야 한다. class Rectangle implements Transformable{ int x, y, width, height; Rectangle(int x, int y, int width, int height){ this.x = x; this.y = y; this.width = width; this.height = height; } public void resize(int width, int height){ this.width = width; this.height = height; } public void moveTo(int x, int y){ this.x = x; this.y = y; } public void moveBy(int xOffset, int yOffset){ this.x += xOffset; this.y += yOffset; } } 위의 클래스를 사용하는 프로그램 public class InterfaceExample4 { public static void main(String[] args) { Rectangle obj = new Rectangle(100, 200, 10, 10); printRectangle(obj); obj.moveTo(25, 35); printRectangle(obj); obj.moveBy(-5, -5); printRectangle(obj); obj.resize(30, 30); printRectangle(obj); } static void printRectangle(Rectangle obj){ System.out.printf("사각형 : 위치%d, %d 크기%d x %d %n", obj.x, obj.y, obj.width, obj.height);
}
} < 결과 > 사각형 : 위치100, 200 크기10 x 10 사각형 : 위치25, 35 크기10 x 10 사각형 : 위치20, 30 크기10 x 10 사각형 : 위치20, 30 크기30 x 30
인터페이스의 다중 상속 인터페이스는 클래스와 달리 다중 상속도 허용한다.
이 중 외형 변경 인터페이스는 크기변경 인터페이스와 색상변경 인터페이스의 기능을 합한 것에 ‘폰트를 바꾼다’ 기능이 하나 더 있을 뿐이다. 이 경우 다중 상속을 통해 인터페이스를 만드는 것이 좋다.
둘 이상의 인터페이스를 상속하는 인터페이스를 선언할 때는 extends절에 상속할 인터페이스의 이름을 모두 쓰고 그 이름을 콤마로 구분해야 한다. [ 크기변경 인터페이스] interface Resizable{ void resize(int width, int height); } [ 색상변경 인터페이스] interface Colorable{ void setForground(String color); void setBackground(String color); } [ 외형변경 인터페이스 선언] interface Changeable extends Resizable, Colorable { void setFont(String font); } |
'myPPT' 카테고리의 다른 글
협동조합 일반론,운동론 (0) | 2012.12.25 |
---|---|
협동조합 의의,기원,발전,원칙,특징,종류,조직 (0) | 2012.12.25 |
소매업에 대한 경영접근 (1) | 2012.11.01 |
문화기술(CT, Culture Technology) --기술결정론(technological determinism) 사회구성론(social construction of technology) (0) | 2012.11.01 |
독일 연방정부의 "첨단기술전략(High-tech Strategy)"의 17개 핵심기술 분야 중 『환경기술』분야를 아래와 같이 요약·정리 (0) | 2012.11.01 |