반응형

출처 : 이것이 자바다

인터페이스

8.1 인터페이스의 역할

v  인터페이스란?

개발 코드와 객체가 서로 통신하는 접점

      개발 코드는 인터페이스의 메소드만 알고 있으면 OK

 

    • 인터페이스의 역할

      개발 코드가 객체에 종속되지 않게 -> 객체 교체할 수 있도록 하는 역할

      개발 코드 변경 없이 리턴값 또는 실행 내용이 다양해 질 수 있음 (다형성)

 

 

8.2 인터페이스 선언

    • 인터페이스 이름 - 자바 식별자 작성 규칙에 따라 작성
    • 소스 파일 생성

      인터페이스 이름과 대소문자가 동일한 소스 파일 생성

    • 인터페이스 선언

 

 

8.2.1인터페이스 선언

인터페이스의 구성 멤버

 

상수필드(Constant Field)

인터페이스는 객체 사용 설명서이므로 런타임 시 데이터를 저장할 수 있는 필드를 선언할 수 없다.

그러나 상수 필드는 선언이 가능하다.

상수는 인터페이스에 고정된 값으로 런타임 시에 데이터를 바꿀 수 없다.

상수를 선언할 때에는 반드시 초기값을 대입

 

추상 메소드(Abstract Method)

추상 메소드는 객체가 가지고 있는 메소드를 설명한 것

매개값이 필요하고, 리턴 타입이 무엇인지만 알려준다.

실제 실행부는 객체(구현 객체)가 가지고 있다.

 

디폴트 메소드(Default Method)

디폴트 메소드는 인터페이스에 선언되지만 사실은 객체가 가지고 있는 인스턴스 메소드라고 생각

디폴트 메소드를 허용하는 이유는 기존 인터페이스를 확장 새로운 기능 추가

 

정적 메소드(Static Method)

자바8부터 작성, 디폴트 메소드와 달리 객체가 없어도 인터페이스만으로 호출이 가능

 

8.2.2 상수 필드 선언

      인터페이스는  상수 필드만 선언 가능

 데이터 저장하지 않음

      인터페이스에 선언된 필드는 모두 public static final

자동적으로 컴파일 과정에서 붙음

      상수명은 대문자로 작성

서로 다른 단어로 구성되어 있을 경우에는 언더 바(_)로 연결

      선언과 동시에 초기값 지정

 static { } 블록 작성 불가 - static {} 으로 초기화 불가

상수 필드 선언

public interface RemoteControl {

           public int MAX_VOLUME = 10;

    public int MIN_VOLUME = 0;

}

 

8.2.3추상 메소드 선언

인터페이스 통해 호출된 메소드는 최종적으로 객체에서 실행

      인터페이스의 메소드는 기본적으로 실행 블록이 없는 추상 메소드로 선언

      public abstract를 생략하더라도 자동적으로 컴파일 과정에서 붙게 됨

 

추상메소드

(메소드 선언부)

(호출 방법만 기술)

재정의된 메소드

(실제 실행 메소드)

 
 
 

 

 

 

 

 

 

 

 

 

 

메소드 선언

public interface RemoteControl {

           public int MAX_VOLUME = 10;

    public int MIN_VOLUME = 0;

   

    //추상 메소드

    public void turnOn();                    //메소드 선언부만 작성 (추상 메소드)

    public void turnOff();           

    public void setVoulume(int volume);       

}

 

8.2.4디폴트 메소드 선언

      자바8에서 추가된 인터페이스의 새로운 멤버

 

      실행 블록을 가지고 있는 메소드

      default 키워드를 반드시 붙여야

      기본적으로 public 접근 제한

생략하더라도 컴파일 과정에서 자동 붙음

메소드 선언

public interface RemoteControl {

           public int MAX_VOLUME = 10;

    public int MIN_VOLUME = 0;

   

    //추상 메소드

    public void turnOn();                    //메소드 선언부만 작성 (추상 메소드)

    public void turnOff();           

    public void setVoulume(int volume);     

   

    //디폴트 메소드

    default void setMute(boolean mute) {        //실행 내용까지 작성

        if(mute) {

            System.out.println("무음 처리합니다.");

        } else {

            System.out.println("무음 해제합니다.");

        }

    }

}

 

8.2.5 정적 메소드 선언

      자바8에서 추가된 인터페이스의 새로운 멤버

 

 
 

 

 

 

 

 

 

 

 

 

메소드 선언

public interface RemoteControl {

           public int MAX_VOLUME = 10;

    public int MIN_VOLUME = 0;

   

    //추상 메소드

    public void turnOn();                    //메소드 선언부만 작성 (추상 메소드)

    public void turnOff();           

    public void setVoulume(int volume);     

   

    //디폴트 메소드

    default void setMute(boolean mute) {        //실행 내용까지 작성

        if(mute) {

            System.out.println("무음 처리합니다.");

        } else {

            System.out.println("무음 해제합니다.");

        }

    }

   

    //정적 메소드

    static void changeBattery() {

        System.out.println("건전지를 교환합니다.");

    }

}

 

8.3 인터페이스 구현

v  구현 객체와 구현 클래스

인터페이스의 추상 메소드 대한 실체 메소드를 가진 객체 = 구현 객체

 

구현 객체

 

 

 

 

 

 

 

 

 

    • 구현 객체를 생성하는 클래스 = 구현 클래스

 

8.3.1 구현 클래스 선언

    • 자신의 객체가 인터페이스 타입으로 사용할 수 있음

      implements 키워드로 명시

 

v  추상 메소드의 실체 메소드를 작성하는 방법

메소드의 선언부가 정확히 일치해야

인터페이스의 모든 추상 메소드를 재정의하는 실체 메소드 작성해야

      일부만 재정의할 경우, 추상 클래스로 선언 + abstract 키워드 붙임

 

구현 클래스

public class Television implements RemoteControl{

    //필드

    private int volume;

   

    @Override

    public void turnOn() {

        System.out.println("TV를 켭니다.");

    }

 

    @Override

    public void turnOff() {

        System.out.println("TV를 끕니다.");

    }

 

    @Override

    public void setVoulume(int volume) {                    //인터페이스 상수를 이용해서 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 볼륨: " + volume);

    }

 

}

 

구현 클래스

public class Audio implements RemoteControl{

    //필드

    private int volume;

   

    @Override

    public void turnOn() {

        System.out.println("Audio를 켭니다.");

    }

 

    @Override

    public void turnOff() {

        System.out.println("Audio를 끕니다.");

    }

 

    @Override

    public void setVoulume(int volume) {                    //인터페이스 상수를 이용해서 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 볼륨: " + volume);

    }

 

}

 

인터페이스 변수에 구현 객체 타입

public class RemoteControlExample {

           public static void main(String[] args) {

                       RemoteControl rc;

                       rc = new Television();

                       rc = new Audio();

           }

}

 

8.3.2 익명 구현 객체

    • 명시적인 구현 클래스 작성 생략하고 바로 구현 객체를 얻는 방법

      이름 없는 구현 클래스 선언과 동시에 객체 생성

 

      인터페이스의 추상 메소드들을 모두 재정의하는 실체 메소드가 있어야

      추가적으로 필드와 메소드 선언 가능하나 익명 객체 안에서만 사용

      인터페이스 변수로 접근 불가

익명 구현 클래스

public class RemoteControlExample {

           public static void main(String[] args) {

                       //RemoteControl rc;

                       //rc = new Television();

                       //rc = new Audio();

                      

                       RemoteControl rc = new RemoteControl() {

                                 

                                  @Override

                                  public void turnOn() {

                                              // TODO Auto-generated method stub

                                             

                                  }

                                 

                                  @Override

                                  public void turnOff() {

                                              // TODO Auto-generated method stub

                                             

                                  }

                                 

                                  @Override

                                  public void setVoulume(int volume) {

                                              // TODO Auto-generated method stub

                                             

                                  }

                       };

           }

}

 

8.3.3다중 인터페이스 구현 클래스

 

 

 

인터페이스

public interface Searchable {

           void search(String url);

}

 

다중 인터페이스 구현 클래스

public class SmartTelevision implements RemoteControl,Searchable{

private int volume;

   

    @Override

    public void turnOn() {

        System.out.println("TV를 켭니다.");

    }

 

    @Override

    public void turnOff() {

        System.out.println("TV를 끕니다.");

    }

 

    @Override

    public void setVoulume(int volume) {                    //인터페이스 상수를 이용해서 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 볼륨: " + volume);

    }

   

    @Override

    public void search(String url) {

        System.out.println(url + "을 검색합니다.");

       

    }

}

 

8.4 인터페이스 사용

인터페이스에 구현 객체를 대입하는 방법

 

 

 

 

public class MyClass {

           //필드

           RemoteControl rc = new Television();

          

           //생성자

           MyClass(RemoteControl rc){

                       this.rc = rc;

           }

          

           //메소드

           void methodA() {

                       //로컬 변수

                       RemoteControl rc = new Audio();

           }

          

           void methodB(RemoteControl rc ) {}

}

 

8.4.1 추상 메소드 사용

 

 

인터페이스 사용

public class RemoteControlExample {

           public static void main(String[] args) {

          

           RemoteControl rc = null;             //인터 페이스 변수 선언

       

        rc = new Television();                //Television 객체를 인터페이스 타입에 대입

        rc.turnOn();

        rc.setMute(true);

       

        rc = new Audio();                    //Audio 객체를 인터페이스 타입에 대입

        rc.turnOn();                       

        rc.setMute(true);

           }

}

 

 

8.4.2 디폴트 메소드 사용

    • 인터페이스만으로는 사용 불가

      구현 객체가 인터페이스에 대입되어야 호출할 수 있는 인스턴스 메소드

    • 모든 구현 객체가 가지고 있는 기본 메소드로 사용

      필요에 따라 구현 클래스가 디폴트 메소드 재정의해 사용

 

구현 클래스

public class Audio implements RemoteControl{

           //필드

    private int volume;

    private boolean mute;

   

    //turnOn() 추상 메소드의 실체 메소드

    @Override

    public void turnOn() {

        System.out.println("Audio를 켭니다.");

    }

    //turnOff() 추상 메소드의 실체 메소드

    @Override

    public void turnOff() {

        System.out.println("Audio를 끕니다.");

    }

    //setVolume() 추상 메소드의 실체 메소드

    @Override

    public void setVoulume(int volume) {                    //인터페이스 상수를 이용해서 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 볼륨: " + volume);

    }

   

    //디폴트 메소드 재정의

    @Override   

    public void setMute(boolean mute) {

        this.mute = mute;

        if(mute) {

            System.out.println("Audio 무음 처리합니다.");

        } else {

            System.out.println("Audio 무음 해제합니다.");

        }

    }

 

}

 

디폴트 메소드 사용

public class RemoteControlExample {

           public static void main(String[] args) {

        RemoteControl rc = null;             //인터 페이스 변수 선언

       

        rc = new Television();                //Television 객체를 인터페이스 타입에 대입

        rc.turnOn();

        rc.setMute(true);

       

        rc = new Audio();                    //Audio 객체를 인터페이스 타입에 대입

        rc.turnOn();                       

        rc.setMute(true);

           }

}

 

8.4.3 정적 메소드 사용

인터페이스로 바로 호출 가능

정적 메소드 사용

public class RemoteControlExample {

           public static void main(String[] args) {

        RemoteControl.changeBattery();

           }

}

 

8.5 타입 변환과 다형성

v  다형성 (p.362~364)

하나의 타입에 여러 가지 객체 대입해 다양한 실행 결과를 얻는 것

다형성을 구현하는 기술

      상속 또는 인터페이스의 자동 타입 변환(Promotion)

      오버라이딩(Overriding)

다형성의 효과

      다양한 실행 결과를 얻을 수 있음

      객체를 부품화시킬 수 있어 유지보수 용이 (메소드의 매개변수로 사용)

 

8.5.1 자동 타입 변환(Promotion)

 

 

 

8.5.2 필드의 다형성

 

 

 

인터페이스

public interface Tire {

           public void roll();    //roll() 메소드 호출 방법 설명

}

구현 클래스

public class HankookTire implements Tire{

    @Override

    public void roll() {

        System.out.println("한국 타이어가 굴러갑니다.");

       

    }

}

 

구현 클래스

public class KumhoTire implements Tire{

 

    @Override

    public void roll() {

        System.out.println("금호 타이어가 굴러갑니다.");

    }

 

}

 

필드 다형성

public class Car {//인터페이스 타입 필드 선언과 초기 구현 객체 대입

    Tire frontLeftTire = new HankookTire();

    Tire frontRightTire = new HankookTire();

    Tire backLeftTire = new HankookTire();

    Tire backRightTire = new HankookTire();

   

    void run() {    //인터페이스에서 설명된 roll() 메소드 호출

        frontLeftTire.roll();

        frontRightTire.roll();

        backLeftTire.roll();

        backRightTire.roll();

    }

 

}

 

필드 다형성 테스트

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();

    }

}

 

8.5.3 인터페이스 배열로 구현한 객체 관리

 

 

 

 

 

 

 

 

 

 

필드 다형성

package textbook.chapter8.exam01;

 

public class Car {

           Tire[] tires = {

                   new HankookTire(),

                   new HankookTire(),

                   new HankookTire(),

                   new HankookTire()       

               };

              

            void run() {

                 for (Tire tire : tires) {

                     tire.roll();

                 }

            }

}

 

필드 사형성 테스트

package textbook.chapter8.exam01;

 

public class CarExample {

           public static void main(String[] args) {

                       Car myCar = new Car();

 

                       myCar.run();

 

                       myCar.tires[0] = new KumhoTire();

                       myCar.tires[1] = new KumhoTire();

 

                       myCar.run();

           }

}

 

8.5.4매개변수의 다형성

    • 매개 변수의 타입이 인터페이스인 경우

      어떠한 구현 객체도 매개값으로 사용 가능

      구현 객체에 따라 메소드 실행결과 달라짐

자동 타입 변환은 필드의 값을 대입할 때도 발생하지만, 주로 메소드를 호출할 때 많이 발생한다.

 

매개 변수의 인터페이스화

public class Driver {

           public void drive(Vehicle vehicle) {

                       vehicle.run();

           }

}

 

인터페이스

public interface Vehicle {

           public void run();//interface는 메소드만 작성한다.

}

 

Vehicleinterface이다.

 

구현 클래스

public class Bus implements Vehicle{

 

           @Override

           public void run() {

                       System.out.println("버스가 달립니다.");

           }

          

}

 

구현 클래스

public class Taxi implements Vehicle {

 

           @Override

           public void run() {

                       System.out.println("택시가 달립니다.");

           }

 

}

 

매개 변수의 다형성 테스트

public class DriverExample {

           public static void main(String[] args) {

                       Driver driver = new Driver();

                      

                       Bus bus = new Bus();

                       Taxi taxi = new Taxi();

                      

                       driver.drive(bus); //자동 타입 변환 Vehicle vehicle =bus;

                       driver.drive(taxi);//자동 타입 변환 Vehicle vehicle =taxi;

           }

}

 

8.5.5 강제 타입 변환(Casting)

인터페이스 타입으로 자동 타입 변환 후, 구현 클래스 타입으로 변환

      필요성: 구현 클래스 타입에 선언된 다른 멤버 사용하기 위해

 

구현클래스 변수 = (구현클래스)인터페이스변수;

인터페이스

public interface Vehicle {

           public void run();//interface는 메소드만 작성한다.

}

 

구현 클래스

public class Bus implements Vehicle{

 

           @Override

           public void run() {

                       System.out.println("버스가 달립니다.");

           }

          

           public void checkFare() {

                       System.out.println("승차요금을 체크합니다.");

           }

}

 

강제 타입 변환

public class DriverExample {

           public static void main(String[] args) {

                       Vehicle vehicle = new Bus();

                       vehicle.run();

                       //vehicle.checkFare(); //vehicle interface에는 checkFare이 없습니다.

                      

                       Bus bus = (Bus)vehicle;//강제 타입 변환

                       bus.run();

                       bus.checkFare();

                      

           }

}

 

8.5.6 객체 타입 확인(instanceof 연산자) (p.375~377)

강제 타입 변환 전 구현 클래스 타입 조사

 

객체 타입 확인

public class Driver {

           public void drive(Vehicle vehicle) {

                       if(vehicle instanceof Bus) {

                                  Bus bus=(Bus)vehicle;

                                  bus.checkFare();//Bus타입으로 강제 타입 변환을 하는 이유

                       }

                       vehicle.run();

           }

}

 

타입인지 확인 하는 것은 instanceof이다. 이것으로 확인하고 처리하는게 좋다.

 

8.6 인터페이스 상속

v  인터페이스간 상속 가능

 

    • 하위 인터페이스 구현 클래스는 아래 추상 메소드를 모두 재정의해야

      하위 인터페이스의 추상 메소드

      상위 인터페이스1의 추상 메소드

      상위 인터페이스2의 추상 메소드

 

    • 인터페이스 자동 타입 변환

      해당 타입의 인터페이스에 선언된 메소드만 호출 가능

 

 

부모 인터페이스

public interface InterfaceA {

           public void methodA();

}

 

부모 인터페이스

public interface InterfaceB {

           public void methodB();

}

 

하위 인터페이스

public interface InterfaceC extends InterfaceA,InterfaceB{

    public void methodC();

}

 

하위 인터페이스 구현

public class ImplementationC implements InterfaceC{

 

    @Override

    public void methodA() {

        System.out.println("methodA() 실행");

    }

 

    @Override

    public void methodB() {

        System.out.println("methodB() 실행");

    }

 

    @Override

    public void methodC() {

        System.out.println("methodC() 실행");

    }

 

}

 

호출 가능 메소드

public class Example {

           public static void main(String[] args) {

        ImplementationC impl = new ImplementationC();

       

        InterfaceA ia = impl;

        ia.methodA();

        System.out.println();        //InterfaceA 변수는 methodA()만 호출 가능

       

        InterfaceB ib = impl;

        ib.methodB();

        System.out.println();         //InterfaceB 변수는 methodB()만 호출 가능

       

        InterfaceC ic = impl;

        ic.methodA();

        ic.methodB();        //InterfaceC 변수는 methodA(),methodB(),methodC() 모두 호출 가능

        ic.methodC();

    }

}

 

 

8.7 디폴트 메소드와 인터페이스 확장

8.7.1 디폴트 메소드의 필요성

v  디폴트 메소드와 확장 메소드 사용하기 (p.379~382)

 

 

interface에 새로운 기능을 추가할 경우 원래의 구현한 메소드에서 오류가 난다. 그래서 이것을 해결하기 위하여 새로운 interface에 디폴트 메소드를 추가한다.

 

기존 인터페이스

public interface MyInterface {

           public void method1();

          

           public default void method2() {

        System.out.println("MyInterface-method2 실행");    //디폴트 메소드

    }

}

 

기존 구현 클래스

public class MyClassA implements MyInterface{

 

    @Override

    public void method1() {

        System.out.println("MyCalssA-method1() 실행");

    }

 

}

 

새로운 구현 클래스

public class MyClassB implements MyInterface{

 

    @Override

    public void method1() {

        System.out.println("MyCalssA-method1() 실행");

    }

    @Override

    public void method2() {

        System.out.println("MyClassB-method2() 실행");        //디폴트 메소드 재정의

    }

 

}

 

디폴트 메소드 사용

public class DefaultMethodExampl {

           public static void main(String[] args) {

                       MyInterface mi1 = new MyClassA();

                       mi1.method1();

                       mi1.method2();

 

                       MyInterface mi2 = new MyClassB();

                       mi2.method1();

                       mi2.method2();

           }

}

 

8.7.2 디폴트 메소드가 있는 인터페이스 상속

    • 부모 인터페이스의 디폴트 메소드를 자식 인터페이스에서 활용 방법

      디폴트 메소드를 단순히 상속만 받음

      디폴트 메소드를 재정의(Override)해서 실행 내용을 변경

      디폴트 메소드를 추상 메소드로 재선언

 

부모 인터페이스

public interface ParentInterface {

           public void method1();

           public default void method2() {/*실행문*/}

}

 

 

자식 인터페이스

public interface ChildInterface1  extends ParentInterface{

           public void method3();

}

 

자식 인터페이스

public interface ChildInterface2  extends ParentInterface{

           @Override

           public default void method2() {/*실행문*/};//재정의

          

           public void method3();

}

 

자식 인터페이스

public interface ChildInterface3  extends ParentInterface{

           @Override

           public void method2();//추상 메소드로 재선언

          

           public void method3();

}

 

사용법

public class ChildExample {

           public static void main(String[] args) {

                       ChildInterface1 ci1 = new ChildInterface1(){

                                  @Override

                                  public void method1() {/*실행문*/}

 

                                  @Override

                                  public void method3() {/*실행문*/}

                                 

                       };

                      

                       ci1.method1();

                       ci1.method2();//ParentInterface method2()호출

                       ci1.method3();

                      

                       ChildInterface2 ci2 = new ChildInterface2(){

                                  @Override

                                  public void method1() {/*실행문*/}//재정의

 

                                  @Override

                                  public void method3() {/*실행문*/}

                                 

                       };

                      

                       ci2.method1();

                       ci2.method2();//ChildInterface2 method2()호출

                       ci2.method3();

                      

                       ChildInterface3 ci3 = new ChildInterface3(){

                                  @Override

                                  public void method1() {/*실행문*/}

                                 

                                  @Override

                                  public void method2() {/*실행문*/}

 

                                  @Override

                                  public void method3() {/*실행문*/}

                                 

                       };

                      

                       ci3.method1();

                       ci3.method2();//ChildInterface3의 구현 객체의 method2()호출

                       ci3.method3();

           }

}

 

 

 

 

 

연습문제

1. 인터페이스에 대한 설명으로 틀린 것은 무엇입니까? (3)

 

1. 인터페이스는 객체 사용 설명서 역할을 한다.

2. 구현 클래스가 인터페이스의 추상 메소드에 대한 실체 메소드를 가지고 있지 않으면 추상 클래스가 된다.

3. 인터페이스는 인스턴스 필드를 가질 수 있다.

4. 구현 객체는 인터페이스 타입으로 자동 변환된다.

 

2. 인터페이스의 다형성과 거리가 먼 것은? (4)

 

1. 필드가 인터페이스 타입일 경우 다양한 구현 객체를 대입할 수 있다.

2. 매개 변수가 인터페이스 타입일 경우 다양한 구현 객체를 대입할 수 있다.

3. 배열이 인터페이스 타입일 경우 다양한 구현 객체를 저장할 수 있다.

4. 구현 객체를 인터페이스 타입으로 변환하려면 강제 타입 변환을 해야 한다.

 

3. 다음은 Soundable 인터페이스입니다. sound() 추상 메소드는 객체의 소리를 리턴합니다.

 

package sec7;

 

public interface Soundable {

    String sound();

}

 

package sec7;

 

public class SoundableExample {

    private static void printSound(Soundable soundable) {

        System.out.println(soundable.sound());

    }

    public static void main(String[] args) {

        printSound(new Cat());

        printSound(new Dog());

    }

}

 

package sec7;

 

public class Cat implements Soundable{

 

    @Override

    public String sound() {

        return "야옹";

    }

    

}

 

package sec7;

 

public class Dog implements Soundable{

 

    @Override

    public String sound() {

        return "멍멍";

    }

    

}

야옹

멍멍

 

4. DaoExample 클래스의 main() 메소드에서 dbWork() 메소드를 호출할 때 OracleDao MySqlDao 객체를 매개값으로 주고 호출했습니다. dbWork() 메소드는 두 객체를 모두 매개값으로 받기 위해 DateAccessObject 타입의 매개 변수를 가지고 있습니다. 실행 결과를 보고 DataAccessObject 인터페이스와 OracleDao, MySqlDao 구현 클래스를 각각 작성해보세요.

 

package sec7;

 

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());

    }

 

}

 

package sec7;

 

public interface DataAccessObject {

 

    public void select();

    public void insert();

    public void update();

    public void delete();

    

}

 

 

package sec7;
 
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에서 삭제");
    }
}

Oracle DB에서 검색

Oracle DB에서 삽입

Oracle DB에서 수정

Oracle DB에서 삭제

MySql DB에서 검색

MySql DB에서 삽입

MySql DB에서 수정

MySql DB에서 삭제

 

5. 다음은 Action 인터페이스입니다. work() 추상 메소드는 객체의 작업을 시작시킵니다.



ActionExample 클래스의 main() 메소드에서 Action의 익명 구현 객체를 만들어 다음과 같은 실행 결과가 나올 수 있도록 박스 안에 들어갈 코드를 작성해보세요.

 

package sec8;

 

public class ActionExample {

 

    public static void main(String[] args) {

        Action action = new Action() {

            

            @Override

            public void work() {

                System.out.println("복사를 합니다.");

            }

        };

        action.work();

    }

 

}​
package sec8;

 

public interface Action {

    void work();

}

복사를 합니다.

 

반응형

' > 이것이 자바다' 카테고리의 다른 글

10. 예외처리  (0) 2020.10.01
09. 중첩 클래스와 중첩 인터페이스  (0) 2020.10.01
07. 상속  (0) 2020.09.29
06. 클래스  (0) 2020.09.28
05. 참조 타입  (0) 2020.09.27
반응형

출처 : 이것이 자바다
상속
7.1 상속 개념
 상속(Inheritance)이란?
현실 세계:
• 부모가 자식에게 물려주는 행위
• 부모가 자식을 선택해서 물려줌
객체 지향 프로그램:
• 자식(하위, 파생) 클래스가 부모(상위) 클래스의 멤버를 물려받는 것
• 자식이 부모를 선택해 물려받음
• 상속 대상: 부모의 필드와 메소드

 상속(Inheritance) 개념의 활용
상속의 효과
• 부모 클래스 재사용해 자식 클래스 빨리 개발 가능
• 반복된 코드 중복 줄임
• 유지 보수 편리성 제공
• 객체 다형성 구현 가능
상속 대상 제한
• 부모 클래스의 private 접근 갖는 필드와 메소드 제외
• 부모 클래스가 다른 패키지에 있을 경우, default 접근 갖는 필드와 메소드도 제외
 extends 키워드
자식 클래스가 상속할 부모 클래스를 지정하는 키워드

//A로 부터 물려받은 필드와 메소드
B b = new B();
b.field1 = 10;
b.method1();

//B가 추가한 필드와 메소드
b.filed2 = "홍길동";
b.method2();

7.2 클래스 상속
현실에서 상속은 부모가 자식을 선택해서 물려주지만, 프로그램에서는 자식이 부모를 선택한다.
class 자식 클래스 extends 부모글래스{
//필드
//생성자
//메소드
}

class SportCar extends Car{
}

 자바는 단일 상속 - 부모 클래스 나열 불가

[CellPhone.java] 부모 클래스

public class CellPhone {
	//필드
    String model;
    String color;
    
    //생성자
    
    //메소드
    void powerOn() {System.out.println("전원을 켭니다.");}
    void powerOff() {System.out.println("전원을 끕니다.");}
    void bell() {System.out.println("벨이 울립니다.");}
    void sendVoice(String message) {System.out.println("자기: " + message);}
    void receiveVoice(String message) {System.out.println("상대방: " + message);}
    void hangUp() {System.out.println("전원을 끊습니다.");}
}

 

[DmbCellPhone.java] 자식 클래스

public class DmbCellPhone extends CellPhone {
	//필드
    int channel;
    
    //생성자
    DmbCellPhone(String model,String color,int channel) {
        this.model = model;     //CellPhone 으로부터 상속받은 필드
        this.color = color;        //CellPhone 으로부터 상속받은 필드
        this.channel = channel;
    }
    
    //메소드
    void turnOnDmb() {
        System.out.println("채널 " + channel + "번 DMB 방송 수신을 시작합니다.");
    }
    
    void changeChannelDmb(int channel) {
        this.channel = channel;
        System.out.println("채널 " + channel + "번으로 바꿉니다.");
    }
    
    void turnOffDmb() {
        System.out.println("DMB 방송 수신을 멈춥니다.");
    }
}

 

[DmbCellPhoneExample.java] 자식 클래스 사용

public class DmbCellPhoneExample {
	public static void main(String[] args) {
		// DmbCellPhone 객체 생성
		DmbCellPhone dmbCellPhone = new DmbCellPhone("자바폰", "검정", 10);

		// CellPhone 으로부터 상속받은 필드
		System.out.println("모델: " + dmbCellPhone.model);
		System.out.println("색상: " + dmbCellPhone.color);

		// DmbCellPhone의 필드
		System.out.println("채널: " + dmbCellPhone.channel);

		// CellPhone으로부터 상속받은 메소드 호출
		dmbCellPhone.powerOn();
		dmbCellPhone.bell();
		dmbCellPhone.sendVoice("여보세요");
		dmbCellPhone.receiveVoice("안녕하세요! 저는 홍길동인데요");
		dmbCellPhone.sendVoice("아~ 예 반갑습니다.");
		dmbCellPhone.hangUp();

		// DmbCellPhone의 메소드 호출
		dmbCellPhone.turnOnDmb();
		dmbCellPhone.changeChannelDmb(12);
		dmbCellPhone.turnOffDmb();
	}
}

7.3 부모 생성자 호출
 자식 객체 생성하면 부모 객체도 생성되는가?
부모 없는 자식 없음
• 자식 객체 생성할 때는 부모 객체부터 생성 후 자식 객체 생성
• 부모 생성자 호출 완료 후 자식 생성자 호출 완료

 명시적인 부모 생성자 호출
부모 객체 생성할 때, 부모 생성자 선택해 호출

• super(매개값,…)
매개값과 동일한 타입, 개수, 순서 맞는 부모 생성자 호출
• 부모 생성자 없다면 컴파일 오류 발생
• 반드시 자식 생성자의 첫 줄에 위치
• 부모 클래스에 기본(매개변수 없는) 생성자가 없다면 필수 작성

[People.java] 부모 클래스

public class People {
	public String name;
	public String ssn;
	
	public People(String name,String ssn) {
		this.name = name;
		this.ssn = ssn;
	}
}

 

[Student.java] 자식 클래스

public class Student extends People {
	public int studentNo;
	
	public Student(String name, String ssn,int studentNo ) {
		super(name, ssn);//부모 생성자 호출
		this.studentNo = studentNo;
	}
}

 

[StudentExample.java] 자식 객체 이용

public class StudentExample {
	public static void main(String[] args) {
		Student student = new Student("홍길동", "12345-1234567", 1);
		//부모에서 물려받은 필드 출력
		System.out.println("name :" + student.name);
		System.out.println("ssn :" + student.ssn);
		System.out.println("studentNo :" + student.studentNo);
	}
}

7.4 메소드 재정의
 메소드 재정의(@Override)
부모 클래스의 상속 메소드 수정해 자식 클래스에서 재정의하는 것
메소드 재정의 조건 (p.295~296)
• 부모 클래스의 메소드와 동일한 시그니처 가져야
• 접근 제한을 더 강하게 오버라이딩 불가
public을 default나 private으로 수정 불가
반대로 default는 public 으로 수정 가능
• 새로운 예외(Exception) throws 불가 (예외처리는 10장 참조)
부모 클래스의 모든 메소드가 자식 클래스에게 맞게 설계되어 있다면 가장 이상적인 상속이지만,
어떤 메소드는 자식 클래스가 사용하기에 적합하지 않을 수도 있다.
이 경우 상속된 일부 메소드는 자식 클래스에서 다시 수정해서 사용해야 한다.
자바는 이런 경우를 위해 메소드 오버라이딩(Overriding) 기능을 제공한다.

 

7.4.1 메소드 재정의(Override)
메소드 오버라이딩은 상속된 메소드의 내용이 자식 클래스에 맞지 않을 경우, 자식 클래스에서 동일한 메소드를 재정의하는 것을 말한다.

메소드가 오버라이딩되었다면 부모 객체의 메소드는 숨겨지기 때문에, 자식 객체에서 메소드를 호출하면 오버라이딩된 자식 메소드가 호출된다.

메소드 오버라이딩 규칙

  • 부모의 메소드와 동일한 시그니처(리턴타입,메소드 이름,매개 변수 리스트)를 가져야 한다.
  • 접근 제한을 더 강하게 오버라이딩할 수 없다.
  • 새로운 예외(Exception) 를 throws 할 수 없다(예외는 10장에서 학습한다).
    [Calculator.java] 부모 클래스

public class Calculator {
	double areaCircle(double r) {
		System.out.println("Calculator 객체의 areaCircle() 실행");
		return 3.14159 * r * r;
	}
}

[Computer.java] 자식 클래스

public class Computer extends Calculator {
	@Override
	double areaCircle(double r) {
		System.out.println("Computer 객체의 areaCircle() 실행");
		return Math.PI * r * r;
	}
}

[ComputerExample.java] 메소드 오버라이딩 클래스

public class ComputerExample {
	public static void main(String[] args) {
		int r = 10;
		Calculator calculator = new Calculator();
		System.out.println("원면적 : " + calculator.areaCircle(r));

		System.out.println();

		Computer computer = new Computer();
		System.out.println("원면적 : " + computer.areaCircle(r)); // 재정의된 메소드 호출
	}
}

  1. 자식 클래스에서 오버라이딩 메소드를 작성할 위치로 입력 커서를 옮긴다
  2. 메뉴에서 [Source Override/Implement Methods...]를 선택한다.
  3. 부모 클래스에서 오버라이딩될 메소드를 선택하고 [OK] 버튼을 클릭한다.

7.4.2 부모 클래스 호출(super)
 부모 메소드 사용(super)
메소드 재정의는 부모 메소드 숨기는 효과 !!
• 자식 클래스에서는 재정의된 메소드만 호출
자식 클래스에서 수정되기 전 부모 메소드 호출 - super 사용
• super는 부모 객체 참조(참고: this는 자신 객체 참조)

자식 클래스에서 부모 클래스의 메소드를 오버라이딩하게 되면,부모 클래스의 메소드는 숨겨지고 오버라이딩된 자식 메소드만 사용된다.
그러나 자식 클래스 내부에서 오버라이딩된 부모 클래스의 메소드를 호출해야 하는 상황이 발생한다면 명시적으로 super 키워드를 붙여서 부모 메소드를 호출할 수 있다.
super는 부모 객체를 참조하고 있기 때문에 부모 메소드에 직접 접근할 수 있다.

[Airplane.java] super변수

public class Airplane {
	public void land() {
		System.out.println("착륙합니다.");
	}
	public void fly() {
		System.out.println("일반비행합니다.");
	}
	public void takeOff() {
		System.out.println("이륙합니다.");
	}
}

[SupersonicAirplane.java] super변수

public class SupersonicAirplane extends Airplane {
	public static final int NORMAL = 1;
    public static final int SUPERSONIC = 2;
    
    public int flyMode = NORMAL;
    
    @Override
    public void fly() {
    	if(flyMode == SUPERSONIC) {
    		System.out.println("초음속비행합니다.");
    	}else {
    		//Airplane 객체의 fly() 메소드 호출
            super.fly();//부모의 함수 호출 할 경우는 super
    	}
    }
}

 

[SupersonicAirplaneExample.java] super변수

public class SupersonicAirplaneExample {
	public static void main(String[] args) {
		SupersonicAirplane sa = new SupersonicAirplane();
		sa.takeOff();
		sa.fly();
		sa.flyMode = SupersonicAirplane.SUPERSONIC;
		sa.fly();
		sa.flyMode = SupersonicAirplane.NORMAL;
		sa.fly();
		sa.land();
	}
}

자주 사용되는 고정값들은 상수를 사용함으로써 가독성을 높여준다.

 

7.5 final클래스와 final 메소드
 final 키워드의 용도
final 필드: 수정 불가 필드
final 클래스: 부모로 사용 불가한 클래스
final 메소드: 자식이 재정의할 수 없는 메소드
final 키워드는 해당 선언이 최종상태이고, 결코 수정될 수 없음을 뜻한다.
final 키워드가 클래스,필드,메소드 선언에 사용될 경우 해석이 조금씩 달라지는데
클래스와 메소드에 final이 지정되면 어떤 효과가 날까?
클래스와 메소드 선언 시에 final 키워드가 지정되면 상속과 관련이 있다.


7.5.1상속할 수 없는 final 클래스
자식 클래스 만들지 못하도록 final 클래스로 생성
public final class 클래스{...}

final 대표적인 예는 String이다.

그래서 다음과 같이 자식 클래스를 만들 수 없다.
public class NewString extends String{...}

[Member.java] 상속할 수 없는 final클래스
public final class Member {

}

[VeryVeryImportantPerson.java] 상속할 수 없는 final 클래스
//Member를 상속할 수 없음
public class VeryVeryImportantPerson extends Member {

}

7.5.2오버라이딩할 수 없는 final 메소드
자식 클래스가 재정의 못하도록 부모 클래스의 메소드를 final로 생성
public final 리턴타입 메소드([매개변수 , ...] ...) {...}

[Car.java] 재정의할 수 없는 final메소드

public class Car {
	// 필드
	public int speed;

	// 메소드
	public void speedUp() {
		speed += 1;
	}

	// final 메소드
	public final void stop() {
		System.out.println("차를 멈춤");
		speed = 0;
	}
}

 

[SportsCar.java] 재정의할 수 없는 final메소드

public class SportsCar extends Car{
    @Override
    public void speedUp() {speed += 10;}
    
    //오버라이딩을 할 수 없음
    @Override
    public void stop() {
        System.out.println("스포츠카를 멈춤");
        speed = 0;
    }
}

 

7.6 protected 접근 제한자
protected는 public과 default 접근 제한의 중간쯤에 해당한다.
같은 패키지에서는 default와 같이 접근 제한이 없지만 다른 패키지에서는 자식 클래스만 접근을 허용
 protected 접근 제한자
상속과 관련된 접근 제한자
• 같은 패키지: default와 동일
• 다른 패키지: 자식 클래스만 접근 허용

protected는 필드와 생성자 ,메소드 선언에 사용될수 있다.

[A.java] protected 접근 제한자

package textbook.chapter7.package1;

public class A {
	protected String field;
	
	protected A() {
		
	}
	
	protected void method() {
		
	}
}

 

[B.java] protected 접근 제한자

package textbook.chapter7.package1;

public class B {
    public void method() {
        A a = new A();; //(O)
        a.field = "value"; //(O)
        a.method();
    }
}

[C.java] protected 접근 제한자 테스트

package textbook.chapter7.package2;

import textbook.chapter7.package1.A;

public class C {
    public void method() {
        A a = new A();            // x
        a.field = "value";          // x
        a.method();                  // x
    }
}

[D.java] protected 접근 제한자

package textbook.chapter7.package2;

import textbook.chapter7.package1.A;

public class D extends A {
	public D() {
		super(); // o
		this.field = "value"; // o
		this.method(); // o
	}
}

7.7 타입 변환과 다형성
 다형성 (多形性, Polymorphism)
같은 타입이지만 실행 결과가 다양한 객체 대입(이용) 가능한 성질
• 부모 타입에는 모든 자식 객체가 대입 가능
자식 타입은 부모 타입으로 자동 타입 변환
• 효과: 객체 부품화 가능

public class Car{
Tire t1 = new HankookTire();
Tire t2 = new KumhoTire();
}


7.7.1 자동타입 변환(Promotion)
자동 타입 변환(Promotion) 은 프로그램 실행 도중에 자동적으로 타입 변환이 일어나는 것을 말한다

 바로 위의 부모가 아니더라도 상속 계층의 상위면 자동 타입 변환 가능
• 변환 후에는 부모 클래스 멤버만 접근 가능 (p.308~310)

 

class A {}

class B extends A {}
class C extends A {}
 
class D extends B {}
class E extends C {}
 
public class PromotionExample {
    public static void main(String[] args) {
        B b = new B();
        C c = new C();
        D d = new D();
        E e = new E();
        
        A a1 = b;
        A a2 = c;
        A a3 = d;
        A a4 = e;
        
        B b1 = d;
        C c1 = e;
        
        //B b3 = e;        //컴파일 에러(상속관계에 있지 않음)
        //C c2 = d;        //컴파일 에러(상속관계에 있지 않음)
        
    }
}

 

부모 타입으로 자동 타입 변환된 이후에는 부모 클래스에 선언된 필드와 메소드만 접근이 가능하다.
비록 변수는 자식 객체를 참조, 변수로 접근 가능한 멤버는 부모 클래스 멤버로만 한정된다.
그러나 예외가 있다
메소드가 자식 클래스에서 오버라이딩되었다면 자식 클래스의 메소드가 대신 호출된다
이것은 다형성(Polymorphism)과 관련이 있기 때문에 매우 중요한 성질

[Parent.java] 자동 타입 변환 후의 멤버 접근

public class Parent {
	public void method1() {
		System.out.println("Parent-method1()");
	}

	public void method2() {
		System.out.println("Parent-method2()");
	}
}
public class Child extends Parent {
	@Override
	public void method2() {
		System.out.println("Child-method2()"); // 재정의
	}

	public void method3() {
		System.out.println("Child-method3()");
	}
}

 

[Child.java] 자동 타입 변환 후의 멤버 접근

public class ChildExample {
	public static void main(String[] args) {
		Child child = new Child();

		Parent parent = child; // 자동 타입 변환
		parent.method1();
		parent.method2(); // 재정의된 메소드가 호출됨
		// parent.method3(); (호출 불가능)

	}
}

 

[ChildExample.java] 자동타입 변환 후의 멤버 접근
public class ChildExample {
public static void main(String[] args) {
Child child = new Child();

    Parent parent = child; // 자동 타입 변환
    parent.method1();
    parent.method2(); // 재정의된 메소드가 호출됨
    // parent.method3(); (호출 불가능)

}

}

7.7.2필드의 다형성
 다형성을 구현하는 기술적 방법
• 부모 타입으로 자동 변환
• 재정의된 메소드(오버라이딩)
다형성이란 동일한 타입을 사용하지만 다양한 결과가 나오는 성질을 말한다.

주로 필드의 값을 다양화함으로써 실행 결과가 다르게 나오도록 구현하는데, 필드의 타입은 변함이 없지만, 실행 도중에 어떤 객체를 필드로 저장하느냐에 따라 실행 결과가 달라질 수 있다.

이것이 필드의 다형성이다.
부모 클래스를 상속하는 자식 클래스는 부모가 가지고 있는 필드와 메소드를 가지고 있으니 사용 방법이 동일할 것이고, 자식 클래스는 부모의 메소드를 오버라이딩(재정의)해서 메소드의 실행내용을 변경함으로써 더 우수한 실행 결과가 나오게 할 수 있다.

class Car{
//필드
Tire frontLeftTire = new Tire();
Tire frontRightTire = new Tire();
Tire backLeftTire = new Tire();
Tire backRightTire = new Tire();
//메소드
void run(){...}
}

 

Car myCar = new Car();
myCar.frontLeftTire = new HankookTire();
myCar.backLeftTire = new KumboTire();
myCar.run();

 

void run(){
	frontLeftTire. roll();
	frontRightTire. roll();
	backLeftTire. roll();
	backRightTire.roll();

}


[Tire.java] 타이어 클래스

package textbook.chapter7.exam01;

public class Tire {
	// 필드
	public int maxRotation; // 최대 회전수(타이어 수명)
	public int accumulatedRotation; // 누적 회전수
	public String location; // 타이어의 위치

	// 생성자
	public Tire(String location, int maxRotation) {
		this.location = location;
		this.maxRotation = maxRotation;
	}

	// 메소드
	public boolean roll() {
		++accumulatedRotation; // 누적 회전수 1증가
		if (accumulatedRotation < maxRotation) {
			System.out.println(location + " Tire 수명: " + // 정상 회전
					(maxRotation - accumulatedRotation) + "회"); // (누적<최대)일 경우 실행
			return true;
		} else {
			System.out.println("*** " + location + " Tire 펑크 ***"); // 펑크(누적=최대)일 경우 실행
			return false;
		}
	}
}

 

[Car.java] Tire를 부품으로 가지는 클래스

package textbook.chapter7.exam01;

public class Car {
	//필드
    Tire frontLeftTire = new Tire("앞왼쪽",6);
    Tire frontRightTire = new Tire("앞오른쪽",2);
    Tire backLeftTire = new Tire("뒤왼쪽",3);
    Tire backRightTire = new Tire("뒤오른쪽",4);        //자동차는 4개의 타이어를 가진다.
    //생성자
    //메소드
    int run() {
        System.out.println("[자동차가 달립니다.]");
        if(frontLeftTire.roll()==false) {stop(); return 1;};        //모든 타이어를 1회 회전시키기 위해 각 Tire
        if(frontRightTire.roll()==false) {stop(); return 2;};    //객체의 roll()메소드를 호출한다.
        if(backLeftTire.roll()==false) {stop(); return 3;};        //false를 리턴하는 roll()이 있을 경우
        if(backRightTire.roll()==false) {stop(); return 4;};        //stop() 메소드를 호출하고 해당 타이어 번호를 리턴한다.
        return 0;
    }
    
    void stop() {
        System.out.println("[자동차가 멈춥니다.]");
    }
}

 

[HankookTire.java] Tire의 자식 클래스

package textbook.chapter7.exam01;

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 + " HankookTire 수명: " + (maxRotation-accumulatedRotation) + "회");
        return true;
        } else {
            System.out.println("*** " + location + " HankookTire 핑크 ***");
            return false;
        }
    }
}

 

[KumhoTire.java] Tire의 자식 클래스

package textbook.chapter7.exam01;

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 + " KumhoTire 수명: " + (maxRotation-accumulatedRotation) + "회");
            return true;
        } else {
            System.out.println("*** " + location + " KumhoTire 펑크 ***");
            return false;
        }
    }
}

[CarExample.java] 실행 클래스

package textbook.chapter7.exam01;

public class CarExample {
	public static void main(String[] args) {
		Car car = new Car(); // Car 객체 생성

		for (int i = 1; i <= 5; i++) { // Car 객체의 run() 메소드 5번 반복 실행
			int problemLocation = car.run();

			switch (problemLocation) {
			case 1:
				System.out.println("앞왼쪽 HankookTire로 교체");
				car.frontLeftTire = new HankookTire("앞왼쪽", 15);
				break;
			case 2:
				System.out.println("앞오른쪽 KumhoTire로 교체");
				car.frontLeftTire = new KumhoTire("앞오른쪽", 13);
				break;
			case 3:
				System.out.println("뒤왼쪽 HankookTire로 교체");
				car.frontLeftTire = new HankookTire("뒤왼쪽", 14);
				break;
			case 4:
				System.out.println("뒤오른쪽 HankookTire로 교체");
				car.frontLeftTire = new HankookTire("뒤오른쪽", 17);
				break;
			}
			System.out.println("-------------------------------"); // 1회전 시 출력되는 내용을 구분
		}
	}
}


결과 =>
[자동차가 달립니다.]
앞왼쪽 Tire 수명: 5회
앞오른쪽 Tire 수명: 1회
뒤왼쪽 Tire 수명: 2회
뒤오른쪽 Tire 수명: 3회


[자동차가 달립니다.]
앞왼쪽 Tire 수명: 4회
* 앞오른쪽 Tire 펑크 *
[자동차가 멈춥니다.]
앞오른쪽 KumhoTire로 교체


[자동차가 달립니다.]
앞오른쪽 KumhoTire 수명: 12회
* 앞오른쪽 Tire 펑크 *
[자동차가 멈춥니다.]
앞오른쪽 KumhoTire로 교체


[자동차가 달립니다.]
앞오른쪽 KumhoTire 수명: 12회
* 앞오른쪽 Tire 펑크 *
[자동차가 멈춥니다.]
앞오른쪽 KumhoTire로 교체


[자동차가 달립니다.]
앞오른쪽 KumhoTire 수명: 12회
* 앞오른쪽 Tire 펑크 *
[자동차가 멈춥니다.]
앞오른쪽 KumhoTire로 교체


7.7.3 하나의 배열로 객체 관리

[Car.java] Tire를 부품으로 가지는 클래스

public class Car {
	 //필드
    Tire[] tires = {
            new Tire("앞왼쪽",6),
            new Tire("앞오른쪽",2),
            new Tire("뒤왼쪽",3),
            new Tire("뒤오른쪽",4),
    };
    
    //메소드
    int run() {
        System.out.println("[자동차가 달립니다.]");
        for(int i=0; i<tires.length;i++) {
            if(tires[i].roll()==false) {
                stop();
                return (i+1);
            }
        }
        return 0;
    }
    
    void stop() {
        System.out.println("[자동차가 멈춥니다.]");
    }
}

 

[CarExample.java] 실행 클래스

public class CarExample {
	public static void main(String[] args) {
	       Car car = new Car();
	        
	        for(int i=1;i<=5;i++) {
	            int problemLocation = car.run();
	            if(problemLocation!=0) {
	                System.out.println(car.tires[problemLocation-1].location + "HankookTire로 교체");
	                car.tires[problemLocation-1] =
	                    new HankookTire(car.tires[problemLocation-1].location,15);
	            }
	            System.out.println("----------------------------------");
	        }
	}
}

 

7.7.4 매개변수의 다형성
 매개변수가 클래스 타입일 경우
• 해당 클래스의 객체 대입이 원칙이나 자식 객체 대입하는 것도 허용
자동 타입 변환
매개변수의 다형성
자동 타입 변환은 필드의 값을 대입할 때에도 발생하지만, 주로 메소드를 호출할 때 많이 발생한다.
메소드를 호출할 때에는 매개 변수의 타입과 동일한 매개값을 지정하는 것이 정석이지만, 매개값을 다양화하기 위해 매개 변수에 자식 타입 객체를 지정할 수도 있다.

Driver driver = new Driver();
Vehicle vehicle = new Vehicle();
driver.drive(vehicle);

우리는 여기서 매우 중요한 것을 하나 알게 되었다.
매개 변수의 타입이 클래스일 경우, 해당 클래스의 객채뿐만 아니라 자식 객체까지도 매개값으로 사용할 수 있다는 것이다.

[Vehicle.java] 부모 클래스
package textbook.chapter7.exam03;

public class Vehicle {
public void run() {
System.out.println("차량이 달립니다.");
}
}

[Driver.java] Vehicle을 이용하는 클래스
package textbook.chapter7.exam03;

public class Driver {
public void drive(Vehicle vehicle) {
vehicle.run();
}
}

[Bus.java] 자식 클래스
package textbook.chapter7.exam03;

public class Bus extends Vehicle {
@Override
public void run() {
System.out.println("버스가 달립니다.");
}
}

[Taxi.java] 자식 클래스

package textbook.chapter7.exam03;

public class Taxi extends Vehicle {
	@Override
	public void run() {
		System.out.println("택시가 달립니다.");
	}
}

[DriverExample.java] 실행 클래스

package textbook.chapter7.exam03;

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);
	}
}

7.7.5 강제 타입 변환(Casting)
 부모 타입을 자식 타입으로 변환하는 것

 조건
• 자식 타입을 부모 타입으로 자동 변환 후, 다시 자식 타입으로 변환할 때
 강제 타입 변환 이 필요한 경우
• 자식 타입이 부모 타입으로 자동 변환
부모 타입에 선언된 필드와 메소드만 사용 가능
• 자식 타입에 선언된 필드와 메소드를 다시 사용해야 할 경우

[Parent.java]부모 클래스

package textbook.chapter7.exam03;

public class Parent {
	public String field1;

	public void method1() {
		System.out.println("Parent-method1()");
	}

	public void method2() {
		System.out.println("Parent-method2()");
	}
}

 

[Child.java] 자식 클래스

package textbook.chapter7.exam03;

public class Child extends Parent {
	public String field2;

	public void method3() {
		System.out.println("Child-method3()");
	}

}

[ChildExample.java] 강제 타입 변환(캐스팅)

package textbook.chapter7.exam03;

public class ChildExample {
    public static void main(String[] args) {
        Parent parent = new Child();
        parent.field1 = "data1";
        parent.method1();
        parent.method2();
        /*
         parent.filed2 = "data2"; (불가능)
         parent.method3();
         */
 
        Child child = (Child) parent;
        child.field2 = "yyy"; //가능
        child.method3(); //가능
    }
}

 

7.7.6 객체 타입 변환(instanceof)
 객체 타입 확인(instanceof) (p.326~329)
부모 타입이면 모두 자식 타입으로 강제 타입 변환할 수 있는 것 아님
• ClassCastException 예외 발생 가능

 먼저 자식 타입인지 확인 후 강제 타입 실행해야 함

[Parent.java]부모 클래스
package textbook.chapter7.exam04;

public class Parent {

}

[Child.java] 자식 클래스
package textbook.chapter7.exam04;

public class Child extends Parent{

}

[InstanceofExampl.java] 객체 타입 확인

package textbook.chapter7.exam04;

public class InstanceofExampl {
	public static void method1(Parent parent) {
		if (parent instanceof Child) {
			Child child = (Child) parent;
			System.out.println("method1 - Child로 변환 성공");
		} else {
			System.out.println("method1 - Child로 변환되지 않음");
		}
	}

	public static void method2(Parent parent) {
		Child child = (Child) parent; // ClassCastException 발생할 가능성 있음
		System.out.println("method2 - Child로 변환 성공");
	}

	public static void main(String[] args) {
		Parent parentA = new Child();
		method1(parentA);
		method2(parentA);

		Parent parentB = new Parent();
		method1(parentB);
		method2(parentB); // 예외 발생
	}
}


결과=>
method1 - Child로 변환 성공
method2 - Child로 변환 성공
method1 - Child로 변환되지 않음
Exception in thread "main" java.lang.ClassCastException: textbook.chapter7.exam04.Parent cannot be cast to textbook.chapter7.exam04.Child
at textbook.chapter7.exam04.InstanceofExampl.method2(InstanceofExampl.java:14)
at textbook.chapter7.exam04.InstanceofExampl.main(InstanceofExampl.java:25)

7.8 추상 클래스
7.8.1 추상 클래스의 개념
 추상(abstract)
• 실체들 간에 공통되는 특성을 추출한 것
예1: 새, 곤충, 물고기 동물 (추상)
예2: 삼성, 현대, LG  회사 (추상)
 추상 클래스(abstract class)
• 실체 클래스들의 공통되는 필드와 메소드 정의한 클래스
• 추상 클래스는 실체 클래스의 부모 클래스 역할 (단독 객체 X)

7.8.2 추상 클래스의 용도
 실체 클래스의 공통된 필드와 메소드의 이름 통일할 목적
• 실체 클래스를 설계자가 여러 사람일 경우,
• 실체 클래스마다 필드와 메소드가 제각기 다른 이름을 가질 수 있음
 실체 클래스를 작성할 때 시간 절약
• 실체 클래스는 추가적인 필드와 메소드만 선언
 실체 클래스 설계 규격을 만들고자 할 때
• 실체 클래스가 가져야 할 필드와 메소드를 추상 클래스에 미리 정의
• 실체 클래스는 추상 클래스를 무조건 상속 받아 작성
7.8.3 추상 클래스 선언
 클래스 선언에 abstract 키워드 사용
• New 연산자로 객체 생성하지 못하고 상속 통해 자식 클래스만 생성 가능

[Phone.java] 추상 클래스

 

public class Phone {
    //필드
    public String owner;
    
    //생성자
    public Phone(String owner) {
        this.owner = owner;
    }
    
    //메소드
    public void turnOn() {
        System.out.println("폰 전원을 켭니다.");
    }
    public void turnOff() {
        System.out.println("폰 전원을 끕니다.");
    }
}

 

[SmartPhone.java] 실체 클래스

public class SmartPhone extends Phone{
    //생성자
    public SmartPhone(String owner) {
        super(owner);
    }
    
    //메소드
    public void internetSearch() {
        System.out.println("인터넷 검색을 합니다.");
    }

}

 

[PhoneExample.java] 추상 클래스

public class PhoneExample {
    public static void main(String[] args) {
      //Phone phone = new Phone();
      SmartPhone smartPhone = new SmartPhone("홍길동");

      smartPhone.turnOn();
      smartPhone.internetSearch();
      smartPhone.turnOff();

  }
}

 

7.8.4 추상 메소드와 오버라이딩(재정의)
메소드 이름 동일하지만, 실행 내용이 실체 클래스마다 다른 메소드
예: 동물은 소리를 낸다. 하지만 실체 동물들의 소리는 제각기 다르다.
구현 방법
• 추상 클래스에는 메소드의 선언부만 작성 (추상 메소드)
• 실체 클래스에서 메소드의 실행 내용 작성(오버라이딩(Overriding))

[Animal.java] 추상 메소드 선언

public abstract class Animal { //추상 클래스
  public String kind;
  public void breathe() {
      System.out.println("숨을 쉽니다.");
  }

  public abstract void sound();            //추상 메소드
}

 

추상 메소드 오버라이딩

public class Dog extends Animal{
  public Dog() {
  	this.kind = "포유류";
  }
  @Override                        //추상 메소드 재정의
  public void sound() {
      System.out.println("멍멍");
  }
}

 

추상 메소드 오버라이딩

public class Cat extends Animal{
  public Cat() {
  	this.kind = "포유류";
  }
  @Override
  public void sound() {
      System.out.println("야옹");
  }
}

 

실행 클래스

public class AnimalExample {
  public static void main(String[] args) {
      Dog dog = new Dog();
      Cat cat = new Cat();
      dog.sound();
      cat.sound();
      System.out.println("-----");
      //변수의 자동 타입 변환
      Animal animal = null;
      animal = new Dog();
      animal.sound();
      animal = new Cat();
      animal.sound();
      System.out.println("-----");
      //메소드의 다형성
      animalSound(new Dog());
      animalSound(new Cat());
  }

  public static void animalSound(Animal animal) {
      animal.sound();        //재정의된 메소드 호출
  }
}

 

연습문제

1. 자바의 상속에 대한 설명 중 틀린 것은 무엇입니까? (1)

 

1. 자바는 다중 상속을 허용한다.

2. 부모의 메소드를 자식 클래스에서 재정의(오버라이딩)할 수 있다.

3. 부모의 private 접근 제한을 갖는 필드와 메소드는 상속의 대상이 아니다.

4. final 클래스는 상속할 수 없고, final 메소드는 오버라이딩할 수 없다.

 

2. 클래스 타입 변환에 대한 설명 중 틀린 것은 무엇입니까? (2)

 

1. 자식 객체는 부모 타입으로 자동 타입 변환된다.

2. 부모 객체는 항상 자식 타입으로 강제 타입 변환된다.

3. 자동 타입 변환을 이용해서 필드와 매개 변수의 다형성을 구현한다.

4. 강제 타입 변환 전에 instanceof 연산자로 변환 가능한지 검사하는 것이 좋다.

 

3. final 키워드에 대한 설명으로 틀린 것은? (1)

 

1.final 클래스는 부모 클래스로 사용할 수 있다.

2.final 필드는 값이 저장된 후에는 변경할 수 없다.

3.final 메소드는 재정의(오버라이딩)할 수 없다.

4.static final 필드는 상수를 말한다.

 

4. 오버라이딩(Overriding)에 대한 설명으로 틀린 것은? (4)

 

1. 부모 메소드의 시그니처 (리턴 타입, 메소드명, 매개 변수)와 동일해야 한다.

2. 부모 메소드보다 좁은 접근 제한자를 붙일 수 없다. (public(부모) -> private (자식))

3. @Override 어노테이션을 사용하면 재정의가 확실한지 컴파일러가 검증한다.

4. protected 접근 제한을 갖는 메소드는 다른 패키지의 자식 클래스에서 재정의할 수 없다.

 

5. Parent 클래스를 상속해서 Child 클래스를 다음과 같이 작성했는데, Child 클래스의 생성자에서 컴파일 에러가 발생했습니다. 그 이유를 설명해보세요.

package sec7;

public class Parent {
public String name;

public Parent(String name) {
    this.name = name;
}

}
package sec7;

public class Child extends Parent{

private int studentNo;

public Child(String name, int studentNo) {
    this.name = name;
    this.studentNo = studentNo;
}

}

 

this.name = name; 부분을 super(name); (부모생성자 호출)으로 고쳐준다.

  1. Parent 클래스를 상속받아 Child 클래스를 다음과 같이 작성했습니다. ChildExample 클래스를 실행했을 때 호출되는 각 클래스의 생성자의 순서를 생각하면서 출력 결과를 작성해보세요.

package sec7;

public class Parent {
public String nation;

public Parent() {
    this("대한민국");
    System.out.println("Parent() call");
}

public Parent(String nation) {
    this.nation = nation;
    System.out.println("Parent(String nation) call");
}

}

package sec7;

public class Child extends Parent{
private String name;

public Child() {
    this("홍길동");
    System.out.println("Child() call");
}

public Child(String name) {
    this.name = name;
    System.out.println("Child(String name) call");
}

package sec7;

public class ChildExample {

public static void main(String[] args) {
    Child child = new Child();
}

Parent(String nation) call
Parent() call
Child(String name) call
Child() call

  1. Tire 클래스를 상속받아 SnowTire 클래스를 다음과 같이 작성했습니다. SnowTireExample 클래스를 실행했을 때 출력 결과는 무엇일까요?

package sec7;

public class Tire {
public void run() {
System.out.println("일반 타이어가 굴러갑니다.");
}
}

package sec7;

public class SnowTire extends Tire{
@Override
public void run() {
System.out.println("스노우 타이어가 굴러갑니다.");
}
}

package sec7;

public class SnowTireExample {

public static void main(String[] args) {
    SnowTire snowTire = new SnowTire();
    Tire tire = snowTire;

    snowTire.run();
    tire.run();
}

스노우 타이어가 굴러갑니다.
스노우 타이어가 굴러갑니다. 

  1. A,B,C,D,E,F 클래스가 다음과 같이 상속 관계에 있을 때 다음 빈칸에 들어올 수 없는 코드는? (2)

자식 객체는 부모타입에 대입 될수 있다.

  1. new B()
  2. (B) new A() //강제 타입 변환
  3. new D() //자동 타입 변환
  4. new E() //자동 타입 변환
반응형

' > 이것이 자바다' 카테고리의 다른 글

09. 중첩 클래스와 중첩 인터페이스  (0) 2020.10.01
08. 인터페이스  (0) 2020.09.30
06. 클래스  (0) 2020.09.28
05. 참조 타입  (0) 2020.09.27
04. 조건문과 반복문  (0) 2020.09.26

+ Recent posts