Java-상속, 다형성, 추상클래스, 내부클래스, 인터페이스

5 분 소요

상속

  • 상속이란? → 부모클래스의 멤버를 자식클래스에 물려주는 행위 (부모는 단 하나, 2개 안됨)
  • 종류
    • 부모클래스 (= 슈퍼클래스) : 상위 클래스
    • 자식클래스 (= 서브클래스) : 하위 클래스 (파생클래스)
  • 장점
    • 상속을 통해서 기존 클래스의 필드와 메소드를 재사용
    • 기존 클래스의 일부도 변경 가능
    • 이미 작성된 검증된 소프트웨어를 재사용
    • 코드의 중복을 방지할 수 있음
  • 형식
      public class SuperCar extends Car {
      }
    
  • 메소드 오버라이딩 (= 메소드 재정의)
    • 자식 클래스가 필요에 따라 상속된 동일한 메소드를 다시 정의하는 것.
    • 부모의 메소드와 동일한 리턴타입, 메소드이름, 매개변수 리스트를 가져야함.
    • 실행문만 다르게 해야함. (나머진 똑같게)
    • 접근제한을 더 강하게 오버라이딩 할 수는 없음.
  • super();
    • 이건 자식클래스의 생성자에 숨겨져있음. / 부모클래스의 디폴트생성자 호출.
    • 자식 생성자가 불리기전에 부모생성자가 먼저 호출됨.
    • 상속을 받으면 자식클래스를 만들 때 부모객체를 먼저 만들고 자식객체를 이어붙여서 만드므로 이런 현상 발생.
    • 근데 부모 클래스에 디폴트생성자가 없다면 super();가 제 기능을 할 수 없어 오류뜸.
    • 이럴 땐 명시적으로 부모생성자의 매개변수 형식에 맞춰서 호출해야한다. (ex : super(500); )

다형성

  • 다형성이란?
    • 여러가지 형태를 가질 수 있는 능력
    • 같은 타입이지만 실행결과가 다양한 객체를 이용할 수 있는 성질
    • 하나의 타입의 참조변수에 여러 객체를 대입함으로써 다양한 기능을 이용할수 있도록 해줌.
    • 조상클래스 타입의 참조변수로 자손 클래스의 인스턴스를 참조할 수 있도록 하는게 다형성! (반대는 안됨.)
    Circle c = new Circle();
    Rectangle r = new Rectangle();  //이건 당연
    
     Shape s1 = new Circle();
     Shape s2 = new Rectangle();  //현재 원과 사각형은 shape클래스의 자식클래스임  
  • 부모타입의 참조변수 sArray로 자식객체들을 모두 참조가능
  • 상속받은 자식객체들을 배열로 관리가능 (서로 다른 객체(Circle, Rectangle)이더라도)
    Shape[] sArray = new Shape[2];
    sArray[0] = new Circle();
    sArray[1] = new Rectangle();
    for (int i = 0; i < sArray.length; i++) {
       sArray[i].draw();
       }
  • 강제타입변환 (하향 형변환)
    • 부모타입을 자식타입으로 형변환하는 것
    • 자식클래스 객체인데 형변환에 의해서 일시적으로 부모클래스 참조 변수에 의하여 참조되고 있는 경우
        Shape s1 = new Circle();
        Shape s2 = new Rectangle();
        s2.draw();  // 이건 rectangle에도 있는 메소드이기 때문에 부모까지 가지도 않음.
        s2.print(); // rectangle에 print()메소드가 없어서 가장 가까운 부모인 shape클래스의 print()를 가져와 실행함.
        //s2.setW(10); // 이게 오류뜨는 이유는 부모에 setW라는 메소드와 w라는 필드가 없기 때문 
        s2.x=10;
        s2.y=110;  // 이건 당연히 가능
        //s2.width=40; // 위의 메소드얘기와 같은 맥락  
      
    • 이러한 상황을 해결할 수 있는 게 강제타입변환
        Rectangle r1 = (Rectangle) s2;  // 이러한 강제타입변환 후 아래 코드 전부 가능
         r1.setW(10);
         r1.w=40;  
      
    • 강제타입변환할 때 주의해야할 점
        Circle c2 = (Rectangle) s2; // 이건 당연히 안된다는 사실 인지  
      
  • instanceof
    • 강제타입변환을 할 때 참조변수가 어떤 자식객체인지 알아야 제대로 형변환을 할 수 있음.
    • 객체의 타입을 확인하는 방법이 instanceof
    • 형식 → 객체 instanceof 클래스타입 (왼쪽 객체가 오른쪽의 인스턴스라면 true 아니면 false)
        //Shape s2 = new Rectangle(); 이었던 코드가
        //Rectangle r1 = (Rectangle) s2; 이렇게 강제타입변환 후에 원래
        //Shape s2 = new Rectangle(); 이게 맞았는지를 기억해내 확인하고 싶을 때
        boolean result = s2 instanceof Rectangle;  // 너 원래 rectangle이었니? 하고 확인한다.
        System.out.println(result);  // 응 맞아! -> true
      
  • 동적바인딩
      class Parent {
              int x = 100;
              public void method() {
                      System.out.println("부모메소드");
              }
      }
      class Child extends Parent {
              int x = 200;
              public void method() {
                      System.out.println("자식메소드");
              }
      }
      public class BindingTest {
              public static void main(String[] args) {
                      Parent p1 = new Child();
                      Child c1 = new Child();
        
                      System.out.println(p1.x);  //100
                      System.out.println(c1.x);  //200
                      p1.method();  //자식메소드
                      c1.method();  //자식메소드
              }
      }
    
    • 필드값은 자식은 자식꺼로 부모는 부모꺼로 나오지만 메소드는 다르다.
    • 자식객체에 오버라이딩된 메소드가 있다면 아무리 부모객체 참조변수라 하더라도 오버라이딩된 자식객체의 메소드를 호출하게 된다.
    • 같은 원리로 자식객체에 오버라이딩된 메소드가 없다면 부모메소드 찾아서 실행한다.

추상클래스

  • 정의 : 클래스의 공통적인 특성을 추출해서 선언한 클래스
  • 선언 형식 : [public] abstract class 클래스명 {필드; 생성자; 메소드;}
  • 용도
    1. 실체 (자식) 클래스들의 공통된 필드와 메소드의 이름을 통일할 목적
    2. 추상클래스로 통일된 양식이 있으면 실체클래스를 작성할 때 시간절약, 간편
  • 특징 : 추상클래스는 자신 객체생성이 불가능-그저 자식클래스의 공통점만 묶기위한 용도이기 때문
  • 추상클래스 vs 실체클래스
    • 추상클래스가 부모이고 실체클래스가 자식으로 구현됨
    • 실체클래스는 추상클래스의 모든 특성을 물려받고, 추가적인 특성을 가질 수 있음.
        public class PhoneTest {
            public static void main(String[] args) {
                  SmartPhone sp = new SmartPhone("김명관");  // 이게 실체클래스
                  sp.turnOn();
                  sp.turnOff();
          
                  //Phone p = new Phone("김명관");  -> 이게 오류뜬다. 추상클래스는 객체생성불가!
            }
      
  • 추상메소드
    • 추상클래스는 실체클래스의 멤버를 통일화하는 데에 목적이 있음
    • 모든 실체클래스들이 가지고 있는 메소드의 실행내용이 동일하다면 추상클래스에 메소드를 지정하는 것이 좋음.
    • 하지만 메소드의 선언만 통일하고 실행내용은 실체클래스마다 달라야하는 경우가 있음.
    • 이럴 때 사용하는 게 추상메소드이며 당연히 추상클래스 내부에서 선언된다.
    • 그래서 형식은 → 실행문이 없기 때문에 블록{}이 없음 → public abstract void sound();
    • 이렇게 추상메소드가 있으면 실체클래스는 반드시 클래스 내부에서 그 추상메소드를 구체화시켜야한다.
    abstract class Animal {
            public abstract void sound();
          public void aadsf () { };


    }
    class Cat extends Animal{
            public void sound() {
                    System.out.println("야용");
            }  // 이렇게 무조건 추상클래스의 추상메소드를 구현해야함.
    }
    class Dog extends Animal {
    
    } // 이렇게 추상메소드를 구현안하면 에러가 뜬다.

내부클래스

  • 내부 클래스란? → 하나의 클래스 안에 다른 클래스를 정의할 수 있음.
  • 형식
      class OuterClass {
              private String secret = "akak";
              class InnerClass {
                        
              }
      }
    
  • 특징 : 외부클래스에 필드가 private으로 선언되어도 내부클래스에선 setter/getter메소드없이 자유롭게 접근이 가능함.
  • 유용한 상황 : 특정 멤버변수를 외부에서 자주 사용한다고 할 때 사용이 효율적이지 못한 경우 그렇다고 그 멤버변수를 public으로 하는건 캡슐화에 어긋남 → 이럴 때 내부클래스선언해서 자유롭게 사용한다. → 읽기도 쉽고 유지보수도 쉬움.
  • 주의사항
    • 외부클래스를 생성한다고 자동으로 내부클래스가 생성될까? → NO!!
    • 외부클래스 생성 시 내부클래스 객체가 생성되게 하고 싶다면 객체화 코드를 작성해야함.
  • 해결방법
      import Day08.OuterClass.InnerClass;
      class OuterClass {
              private String secret = "akak";
              public OuterClass() {
                      // 내부클래스 객체화
                      InnerClass in = new InnerClass();
                      in.method(); //  outerclass의 private 필드 접근가능
              }
              class InnerClass {
                      public InnerClass() {
                              System.out.println("내부클래스 생성자임!");
                      }
                      public void method() {
                              System.out.println(secret);
                      }
              }
      }
      public class OuterClassTest {
              public static void main(String[] args) {
                      OuterClass out = new OuterClass();
                      InnerClass in = out.new InnerClass();  // import 해줘야함
                      in.method(); //  outerclass의 private 필드 접근가능
              }
      }
    

    인터페이스

  • 인터페이스란?
    • 추상클래스의 극단적인 경우임. 추상클래스보다 더 엄격한 애임.
    • 인터페이스는 무조건 추상메소드로만 이루어짐.
    • 이 추상메소드들은 특정 인터페이스를 따르는 클래스들을 위한 요구조건 역할을 함. (규약생성)
    • 당연히 객체로 생성안됨
    • 상속은 무조건 하나이지만, 인터페이스는 다중 인터페이스가 가능
  • 사용 이유
    • 시스템을 구성하는 클래스들을 효율적으로 디자인할 수 있음. (틀을 잘 짜놓는 역할이 인터페이스)
    • 정형화된 틀 안에서 클래스를 개발할 수 있음.
    • 서로 연관이 없는 클래스들끼리 관계를 맺을 수 있음.
  • 사전적 의미
    • 두 가지 주제 혹은 시스템이 상호작용하기 위한 장치.
  • 인터페이스 형식
      public interface RemoteControl {
              public void turnOn();
              public void turnff();
              public void setVolume(int vol);
      }
    
  • 구현 클래스 형식
      public class Audio implements RemoteControl{
              int vol;
              public void turnOn() {
              System.out.println("tv가 켜졌습니다.");
              }
              public void turnff() {
              System.out.println("tv가 꺼졌습니다.");
              }
              public void setVolume(int vol) {
                      this.vol = vol;
                      System.out.println("tv의 볼륨은 " + vol + "입니다.");
              } // 이렇게 무조건 추상메소드들을 오버라이딩해야함.
      }  
    
  • test 하기
      public class RemoteControlTest {
              public static void main(String[] args) {
                      RemoteControl rc = new Audio(); // 이런 식으로 객체 생성해야함. (다형성)
                      rc.turnOff();
                      rc.turnOn();
                      rc.setVolume(10);
              }
      }
    

카테고리:

업데이트:

댓글남기기