본문 바로가기

Programming/자바

자바 재정리 - 상속

상속

: 부모클래스가 자식클래스에 필드, 메소드 물려주는 것

 

부모클래스 내 private 필드, 메소드는 상속되지 않음 (접근할 수 없어서)

(자바) 다중 상속 불가 => 단 하나의 부모 클래스만 상속 가능

 

 

 

 

  •  
public class Account {
 
    static final String BANKNAME = "신한은행";
    String accountNo;    
    String ownerName;  
    int balance;
 
    public Account(String accountNo, String ownerName, int balance) {
        this.accountNo=accountNo;   
        this.ownerName=ownerName;
        this.balance=balance;
    }
    
    public Account() {
    }
    
    void deposit (int amount) {  
        balance+=amount;
    }    
 
    int withdraw(int amount) throws Exception {  
        if (balance<amount) {
            throw new Exception("잔액 부족");
        } 
        balance-=amount;
        return amount;
    }    
}


//Account 클래스 상속
public class CheckingAccount extends Account {   
    //필드 추가
    String cardNo;
    
    //생성자
    public 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 (!this.cardNo.equals(cardNo)) {
            throw new Exception("카드번호가 일치하지 않습니다.");
        }else {
            return withdraw(amount);
        }
    }
}


public class CheckingAccountEx1 {
 
    public static void main(String[] args) {
        CheckingAccount chulsu = new CheckingAccount("22-333", "김철수", 1000, "1111-2222-3333");
        chulsu.deposit(30000); //Account 메소드 상속했으므로 객체 생성 시 사용 가능
        try {
            int paidAmount = chulsu.pay("1111-2222-3333", 11000);
            System.out.println("지불액 : "+paidAmount);
            System.out.println("잔액 : "+chulsu.balance);
        }catch(Exception e) {
            System.out.println(e.getMessage());
        }
    }
}

//
지불액 : 11000
잔액 : 20000

 

 

 

 

  • 부모 생성자 호출 (super)

객체는 클래스 생성자 호출해야만 생성됨 & 부모 객체 생성해야 자식 객체 생성 가능 

=> 자식 클래스 객체 생성하려면 부모 생성자 호출해야 함

 

 

부모생성자는 자식생성자의 맨 첫 줄에서 호출됨

 

 

부모의 기본생성자 있을 때 => super();

부모의 기본생성자 없을 때(=매개변수 있는 생성자만 있을 때) => super(매개값,..);

 

 

 

 

메소드 오버라이딩 (메소드 재정의)

: 자식클래스에서 상속받은 메소드를 수정해 재정의하는 것

=> 자식객체 메소드 호출 시 오버라이딩된 메소드 호출됨

 

 

주의

1. 접근제한을 더 강하게 오버라이딩할 수 없음 (ex. 부모: public => 자식: default X)

2. 새로운 예외 throws 할 수 없음

 

 

cf. 메소드 오버로딩 : 같은 이름으로 여러 메소드 생성 (매개변수 개수, 타입, 순서 다를 때)

 

 

  •  
public class CreditLineAccount extends Account{  //Account 상속
    //필드
    int creditLine; //마이너스 한도
    
    //생성자
    public CreditLineAccount(String accountNo, String ownerName, int balance, int creditLine) {
        super(accountNo, ownerName, balance);  
        this.creditLine=creditLine;
    }
    
    //메소드
    int withdraw(int amount) throws Exception {  //withdraw 재정의 => 메소드 오버라이딩 (deposit()은 그대로)
        if (balance+creditLine<amount) {
            throw new Exception("인출이 불가능합니다.");
        }
        balance-=amount;
        return amount;
    }
}

 

 

 

 

  • 부모 메소드 호출(super)

메소드 오버라이딩한 자식클래스에서 기존 부모클래스 메소드 호출해야 하는 경우

 

 

super.메소드명();

 

 

 

 

 

final 클래스, 메소드

final : 수정 불가능한 최종값

 

 

1. final 클래스 => 최종 클래스이므로 자식클래스 생성(상속) 불가

 

2. final 메소드 => 최종 메소드이므로 재정의(오버라이딩) 불가

 

 

 

 

다형성

: 같은 타입의 다양한 객체를 이용할 수 있는 성질

 

부모타입에 모든 자식객체 대입 가능
ex. 같은 규격(타입)이면 금호타이어, 한국타이어(객체) 모두 교체 가능

 

 

 

 

  • 클래스의 다형성

1. 자동 형변환 : 자식클래스 타입 객체를 부모클래스 타입으로 형변환

ex. 부모클래스 A = new 자식클래스();

 

부모클래스로 선언, 자식클래스로 생성 => 결과적으로 부모클래스 타입 산출됨

부모클래스에 존재하는 필드, 메소드에만 접근 가능

부모, 자식클래스에 동시에 존재하는 메소드(오버라이딩한 메소드)는 자식클래스의 메소드 사용

 

 

2. 강제 형변환 : 부모 객체가 자식 타입으로 강제 타입 변환 <= (자동 변환 후 자식객체 필드/메소드 필요할 때 강제변환)

ex. 자식클래스 B =(자식) 부모클래스 A;  


자식 -> 부모 -> 자식 클래스로 재변환하는 경우에만 가능

부모클래스에 기본 생성자 없으면 자식클래스 기본생성자 생성 불가

 

 

  •  
public class PolymorphismEx1 {
 
    public static void main(String[] args) {
        
       Account chulsu1 = new Account();
       CheckingAccount gildong1=new CheckingAccount(); //(CheckingAccount에 디폴트 생성자 추가함) 
       CreditLineAccount chanho1 = new CreditLineAccount();
        
        Account chulsu = new CheckingAccount();  //자동 형변환
        //선언은 Account, 생성은 CheckingAccount => 결과: Account 타입
        //선언된 클래스의 메소드만 사용 가능 ex. Account (withdraw, deposit)
        Account gildong = new CreditLineAccount();
        //오버라이드한 메소드 이용 가능 (Credit의 withdraw)
        Account chanho = new DokdoAccount(null, null, 0, 0);
        //오버라이드 메소드 이용 가능 (Dokdo의 deposit)
        
        CheckingAccount younghee = (CheckingAccount)chulsu; //강제형변환 
    }
}

 

 

  • 객체 타입 확인 (instanceof)

강제 형변환 가능한지 검사 (자식->부모 변환 여부 확인)

부모타입 변수가 부모객체 참조하면 불가

 

 

public class InstanceOfEx{

    public void test(Parent parent){ //Parent, Child 타입 모두 파라미터로 들어올 수 있음
        if (parent instanceof Child){ //Child 타입으로 변환 가능한지 검사
        //매개변수가 참조하는 객체가 Child 타입인지 검사
            Child child = (Child) parent;
            System.out.println("Child로 변환 성공");
        } else {
        	System.out.println("Child로 변환 실패");
        }
    }

	public static void main(String[] args){
            Parent parentA = new Child(); //자동형변환
            test(parentA);  //"Child로 변환 성공" //강제형변환

            Parent parentB = new Parent(); 
            test(parentB);  //"Child로 변환 실패"
    }

}

 

 

 

 

  • 필드의 다형성
  •  
public class Tire {
    //필드
    public int maxRotation;  //타이어 수명
    public int accumulatedRotation;  //누적 회전수
    public String location;  //타이어 위치 (4곳)
    
    //생성자
    public Tire(String location, int maxRotation) {
        this.location=location;
        this.maxRotation=maxRotation;
    }
    
    //메서드
    public boolean roll() {
        accumulatedRotation++;
        if (accumulatedRotation<maxRotation) {
            System.out.println(location+" 타이어의 수명 : "+(maxRotation-accumulatedRotation)+"회");
            return true;
        }else {
        System.out.println("*** "+location+" 타이어 펑크 ***");
        return false;
        }
    }
}


public class Car {
 
        //필드
        Tire frontLeftTire = new Tire("앞 왼쪽", 7);
        Tire frontRightTire = new Tire("앞 오른쪽", 3);
        Tire backLeftTire = new Tire("뒤 왼쪽", 5);
        Tire backRightTire = new Tire("뒤 오른쪽", 4);
        
        //default 생성자
        
        //메서드
        int run() {
            System.out.println("자동차가 달립니다. 씽씽");
            if(frontLeftTire.roll()==false) {
                stop();
                return 1;
            }
            if(frontRightTire.roll()==false) {
                stop();
                return 2;
            }
            if(backLeftTire.roll()==false) {
                stop();
                return 3;
            }
            if(backRightTire.roll()==false) {
                stop();
                return 4;
            }
            return 0; //펑크 안 날 때
        }
        
        void stop() {
            System.out.println("자동차가 멈춥니다.");
        }        
}

 

public class HankookTire extends Tire {
    //필드 상속
    
    //생성자
    public HankookTire(String location, int maxRotation) {
        super(location, maxRotation);
    }
    
    //메소드 오버라이드
    @Override
    public boolean roll() {
        accumulatedRotation++;
        if (accumulatedRotation<maxRotation) {
            System.out.println(location+" 한국타이어의 수명 : "+(maxRotation-accumulatedRotation)+"회");
            return true;
        }else {
        System.out.println("*** "+location+" 한국타이어 펑크 ***");
        return false;
        }
    }
}


public class KumhoTire extends Tire {
    //필드 상속
    
    //생성자
    public KumhoTire(String location, int maxRotation) {
        super(location, maxRotation);
    }
    
    //메소드 오버라이드
    @Override
    public boolean roll() {
        accumulatedRotation++;
        if (accumulatedRotation<maxRotation) {
            System.out.println(location+" 금호타이어의 수명 : "+(maxRotation-accumulatedRotation)+"회");
            return true;
        }else {
        System.out.println("*** "+location+" 금호타이어 펑크 ***");
        return false;
        }
    }
}

 

public class CarEx1 {
 
    public static void main(String[] args) {
        Car csMyCar = new Car();  //인스턴스 객체 생성
        for (int i=1;i<=5;i++) { //5번 반복
            int problemLocation = csMyCar.run();  
            switch (problemLocation) {  // 펑크 안 나면 0
            case 1:
                System.out.println("앞 왼쪽 금호타이어로 교체");
                csMyCar.frontLeftTire=new KumhoTire("앞 왼쪽", 15);  //필드의 다형성 
                //KumhoTire 객체를 Car의 Tire 타입 frontLeftTire 필드에 대입
                break;
            case 2:
                System.out.println("앞 오른쪽 한국타이어로 교체");
                csMyCar.frontRightTire=new HankookTire("앞 오른쪽", 10);  //필드의 다형성
                break;
            case 3:
                System.out.println("뒤 왼쪽 한국타이어로 교체");
                csMyCar.backLeftTire=new HankookTire("뒤 왼쪽", 13);  //필드의 다형성
                break;
            case 4:
                System.out.println("뒤 오른쪽 금호타이어로 교체");
                csMyCar.backRightTire=new KumhoTire("뒤 오른쪽", 12);  //필드의 다형성
                break;            
            }//switch
            System.out.println("-----------------");
        }//for
    }
}

 

 

 

 

cf. Car 클래스 배열로 축약하기

public class Car {

        //Tire frontLeftTire = new Tire("앞 왼쪽", 7);
        //Tire frontRightTire = new Tire("앞 오른쪽", 3);
        //Tire backLeftTire = new Tire("뒤 왼쪽", 5);
        //Tire backRightTire = new Tire("뒤 오른쪽", 4);
        
        Tire[] tires = {
            new Tire("앞 왼쪽", 7),
            new Tire("앞 오른쪽", 3),
            new Tire("뒤 왼쪽", 5),
            new Tire("뒤 오른쪽", 4)
        }
        
        int run() {
            System.out.println("자동차가 달립니다. 씽씽");
            /*if(frontLeftTire.roll()==false) {
                stop();
                return 1;
            }
            if(frontRightTire.roll()==false) {
                stop();
                return 2;
            }
            if(backLeftTire.roll()==false) {
                stop();
                return 3;
            }
            if(backRightTire.roll()==false) {
                stop();
                return 4;
            }
            return 0; */
            for (int i=0;i<tires.length;i++){
            	if (tires[i].roll()==false){
                	stop();
                    return (i+1);
                }
            }
            return 0;
        }
        
        void stop() {
            System.out.println("자동차가 멈춥니다.");
        }        

}

 

 

 

 

  • 매개변수의 다형성
  •  
public class Driver {
    //메소드
    void Drive(Vehicle vehicle) {  //Vehicle 타입의 클래스 받음
        vehicle.run();
    }
}
 
 
public class Vehicle {
    //메서드
    public void run() {
        System.out.println("차량이 달립니다.");
    }
}
 
 
public class Bus extends Vehicle {
    @Override
    public void run() {
        System.out.println("버스가 달립니다.");
    }
}
 
 
public class Taxi extends Vehicle {
    @Override
    public void run() {
        System.out.println("택시가 달립니다.");
    }
}
 
 
public class DriverEx1 {
 
    public static void main(String[] args) {
        Driver chulsu = new Driver();
        Taxi taxi = new Taxi();
        Bus bus = new Bus();
        chulsu.Drive(taxi);  //오버라이딩한 메소드 실행
        chulsu.Drive(bus);   //
    }
}

//

택시가 달립니다.
버스가 달립니다.

 

 

 

 

추상 클래스

: 실체 클래스 간 공통 특성 추출해 선언한 클래스 

=> 인터페이스와 유사, 인터페이스가 더 자주 쓰임

 


실체 클래스들의 필드, 메소드 이름 통일시킴
실체 클래스 작성 시 시간 절약
=>
메소드 이름만 추상 클래스에서 미리 정의해두고, 통일된 이름 다른 클래스들에서 사용하도록

 


추상클래스 내에는 반드시 추상 메서드(메소드 본체 없음)가 있음 & 자식 클래스는 추상 메서드 내부에 코드 작성해야 함
new 로 객체 생성 불가 => 상속 통해서만 자식 클래스 생성 가능

 


틀만 만들고 구현은 자식클래스에 넘김

 

 

  •  
public abstract class Animal { //추상 클래스
    String name;
    
    abstract void move();  //추상 메소드=> 메서드 본체(중괄호 열고 기능 적는 것)가 없음
}
 
 
public class Tiger extends Animal{ //자식 클래스
    int age;   
    @Override
    void move() { //추상메소드 오버라이딩
        System.out.println("네 발로 이동");
    }
}
 
 
public class Eagle extends Animal{ //자식 클래스
    String home;
    @Override
    void move() { //
        System.out.println("날개로 이동");
    }
}
 
 
public class AnimalEx1 {
 
    public static void main(String[] args) {
        Tiger tiger1 = new Tiger();
        Eagle eagle1 = new Eagle();
        
        tiger1.name = "대한이";
        tiger1.age = 2;
        System.out.println(tiger1.name+"는 "+tiger1.age+"살 입니다.");
        tiger1.move();
        
        eagle1.name = "대머리";
        eagle1.home = "소나무 둥지";
        System.out.println(eagle1.name+"는 "+eagle1.home+"에 삽니다.");
        eagle1.move();
    }
}

//

대한이는 2살 입니다.

네 발로 이동

대머리는 소나무 둥지에 삽니다.

날개로 이동