Inheritance

man soup·2020년 6월 10일
0

자바 문법

목록 보기
5/15

Inheritance

  • 자바에서 클래스는 다른 클래스로부터 파생될 수 있다. 이때 다른 클래스의 필드와 함수들을 상속받는다.
  • sub class : 다른 클래스로부터 파생된 클래스 ( extended class, derived class, child class 라고도 부름)
  • super class : subclass를 파생시킨 클래스 ( base class, parent class )
  • Object 클래스( 부모 클래스 존재x)를 제외한 모든 클래스는 오직 하나의 부모 클래스를 가진다. 명시적인 부모 클래스가 없다면 암시적으로 Object가 부모이다.
  • 모든 클래스의 조상은 Object 클래스이다.
  • 상속은 간단하지만 강력하다.
    • 새로운 클래스를 만드려고 할때 이미 원하는 코드를 가진 클래스가 존재한다면, 만드려는 새로운 클래스를 그 클래스로부터 파생시킬 수 있다.
    • 이렇게 하므로써 기존 클래스의 필드와 함수를 새롭게 작성하고 디버그할 필요없이 재사용할 수 있다.
  • 자식클래스는 부모클래스로부터 모든 맴버(필드,함수,nested class)를 상속 받는다.
  • 생성자는 member가 아니므로 상속받지 않지만 부모클래스의 생성자를 자식클래스에서 깨울 수 있다.

The Java Platform Class Hierarchy

  • java.lang 패키지에 정의된 Object 클래스는 모든 클래스들의 공통된 행동들을 정의하고 구현한다.
  • Object 클래스가 가장 general한 클래스이고 바닥으로 갈 수록 특징적인 행동요소를 제공한다.

What You Can Do in a Subclass

  • subclass는 자신이 어느 패키지에 존재하던 부모의 public 과 protected 맴버들을 모두 상속받는다.
  • 만약 부모와 같은 패키지에 존재한다면 package-private 맴버들도 상속받는다.
  • 상속 받은 맴버들을 그대로 사용할 수, 대체할 수도, 숨길 수도, 새로운 맴버들들로 보완할 수 도 있다.
  • 상속 받은 필드들은 다른 모든 필드들과 같이 직접 사용할 수 있다.
  • (추천x) 부모클래스의 필드와 같은 이름의 필드를 선언해 부모필드를 숨길 수 있다.
  • 상속받은 함수를 직접적으로 사용할 수 있다.
  • 부모의 인스턴스 함수와 같은 signature(함수이름+ parameter)의 인스턴스 함수를 만듦으로 override 할 수 있다.
  • 부모의 static 함수와 같은 signature의 static 함수를 만듦으로 hiding 할 수 있다.
  • super 키워드를 사용해 부모의 생성자를 깨우는 생성자를 만들 수 있다.

Private Members in a Superclass

  • 자식클래스는 부모의 private 맴버들을 상속받지 않는다.
  • 그러나 부모의 public 또는 protected 함수가 private 필드를 접근한다면 서브클래스도 그 함수들을 통해 접근할 수 있다.
  • nested class는 enclosing class의 private 맴버에 접근할 수 있다. 그러므로 만약 public 또는 protected nested class를 상속받은 subclass는 superclass의 모든 private 맴버들에 간접적인 접근을 할 수 있다.

Casting Objects

  • object는 어떤 클래스로부터 인스턴화된 것이고 그 클래스 타입이다.
    • MountainBike myBike = new MountainBike();
    • myBike는 MountainBike 타입이다.
    • MountainBike는 Bicycle 과 Object로부터 내려온다.
    • 그러므로 MountainBike는 Bicycle이면서 Object이다.
    • Bicyle 과 Object가 불려진 곳에 사용될 수 있다.
    • 반대상황은 불가능 ( Bicyle은 MountainBike일 수도 있지만 아닐 수 도 있다.)
  • 캐스팅은 상속과 구현이 허용하는 객체 중 다른 유형 대신 한 유형의 객체 사용을 보여준다.
    • 암시적 캐스팅
      • Object obj = new MountainBike();
      • obj는 Object인 동시에 MountainBike이다.
    • 명시적 캐스팅
      • MountainBike myBike = obj;
      • obj가 MountainBike인지 컴파일러는 알 수 없어 컴파일 에러를 낸다.
      • 이 경우 사용자가 컴파일러에게 obj에 MountainBike를 할당할 것을 약속할 수 있다.
      • MountainBike myBike = (MountainBike)obj;
      • 이 캐스트는 런타임 체크를 삽입시켜 obj가 MountainBike로 할당됬는지 확인한다. 그러므로써 컴파일러는 안전하게 obj가 MountainBike라고 가정한다.
      • 만약 런타임에 obj가 MountainBike가 아니면 예외가 thrown된다.
  • instanceof 연산자를 통해 특정 객체의 타입을 테스트 할 수 있다.
    • if (obj instanceof MountainBike) {
      MountainBike myBike = (MountainBike)obj;
      }
    • obj가 MountainBike인지 instanceof 연산자가 확인해 런타임 예외가 발생하지 않을 것을 확인 후 캐스트한다.

Multiple Inheritance of State, Implementation, and Type

  • 클래스와 인터페이스를 가장 큰 차이점은 필드의 유무이다.
  • 다른 차이점은 클래스를 인스턴스화 해 객체를 만들 수 있지만 인터페이스는 불가능 하다.
  • 객체는 자신의 상태를 필드에 저장하는데 필드는 클래스에 정의되있다.
  • 자바가 하나 이상의 클래스 extend를 허가하지 않는 이유중 하나는 Multiple Inheritance of State 이슈때문이다.
    • Multiple Inheritance of State : 여러 클래스로부터 필드를 상속 받는 것
    • 예를들어 어떤 클래스가 여러 클래스를 상속받은 경우 생각해보자
    • 어떤 객체를 그 클래스로 부터 인스턴화해 생성했을때 그 객체는 그 클래스의 모든 부모들의 필드를 상속받는다.
    • 어떤 함수나 다른 부모클래스들의 생성자가 같은 필드를 초기화한다면 어떤일이 일어날까?
    • 어떤 함수나 생성자가 우선순위에 있어야 할까?
    • 인터페이스는 필드가 존재하기 않기 때문에 이러한 문제가 없다.
  • Multiple inheritance of implementation는 여러 클래스로 부터 함수 정의를 상속받는 능력을 말한다.
    • 이 타입의 다중 상속 문제로 name conflict와 ambiguity가 존재한다.
    • 이런 다중 상속을 지원하는 컴파일러들이 같은 이름의 함수를 가진 부모클래스들을 만났을때, 가끔 어떤 멤버나 함수에 접근 또는 깨울지 정하지 못한다.
    • 또한 프로그래머가 부모클래스에 새로운 함수를 추가하므로 의도치않게 name conflict를 일으킬 수 있다.
    • Default Method에서 다중 상속 구현 중 하나의 형태를 소개했다.
  • Multiple inheritance of implementation
    • 자바는 타입의 다중 상속을 지원한다.
    • 하나 이상의 인터페이스를 구현하는 능력을 말한다.
    • 객체는 여러 타입을 가질 수 있다.
      • 자신의 클래스 타입, 그 클래스가 구현한 여러 인터페이스의 타입
      • 이 의미는 만약 변수가 인터페이스 타입으로 선언되었다면 그것은 그 인터페이스를 구현한 클래스로부터 인스턴스화된 어느 객체도 참조할 수 있다는 것.
  • 다중 구현 상속과 마찬가지로 클래스는 확장하는 인터페이스에 정의 된 메소드(static 또는 default)의 다른 구현을 상속 할 수 있다.

Overriding and Hiding Methods

Instance Methods

  • superclass의 인스턴스 함수와 같은 signature와 return 타입인 subclass의 인스턴스 함수는 superclass 인스턴스 함수를 override 한다.

  • subclass의 함수를 override하는 능력은 subclass가 자신과 행동이 비슷한 superclass로부터 상속받고 필요할 경우 행동을 변경하게 해준다.

  • 오버라이딩 함수는 대체하는 함수와 같은 함수명, 같은 파라미터의 개수와 타입, 같은 리턴타입을 가진다.

  • 오버라이딩 함수의 리턴 타입은 오버라이딩하는 함수의 리턴 타입의 subtype일 수 있다. 이 subtype은 covariant return type이라고 부른다.

  • 함수를 오버라이딩할 때, @Override 어노테이션을 사용함으로써 컴파일러에게 superclass의 함수를 오버라이딩을 알릴 수 있다. 어떤 이유로 컴파일러가 오버라이딩 함수가 superclass에 존재하지 않음을 발견하면 에러를 발생시킨다.

Static Method

  • subclass가 superclass의 static 함수와 같은 함수 시그니처를 가진 static 함수를 정의하면, subclass에 존재하는 그 함수는 superclass의 함수를 숨긴다.

  • static 함수를 숨기는 것과 instance 함수를 override하는 것의 차이는 중요한 함축을 가진다.

    • overridden된 instance 함수중 호출되는 함수는 subclass의 함수이다.
    • 숨겨진 static 함수중 호출되는 함수는 superclass또는 subclass에서 호출됬는지에 따라 결정된다.
    • Cat myCat = new Cat();
           Animal myAnimal = myCat;
           Animal.testClassMethod();
           myAnimal.testInstanceMethod();
    • Cat의 superclass인 animal 타입의 myAnimal 객체를 통해 testInstanceMethod() 호출해도 Cat에서 override한 testInstanceMethod()가 호출된다.
    • Animal을 통해 testClassMethod()를 호출하면 Animal에서 정의한 testClassMethod가 호출된다.

Interface Method

  • 인터페이스에 존재하는 Default 함수와 abstract 함수는 instance 함수와 같은 방식으로 상속된다.

  • 그러나 클래스나 인터페이스의 supertype이 같은 시그니처를 가진 다중 default 함수들을 제공하면, 자바 컴파일러는 상속 규칙을 적용해 name conflict 문제를 해결한다.

    • 2가지 규칙이 존재한다.
    • 인터페이스의 default 함수보다 instance 함수가 우선시 된다.
    • 이미 다른 후보들에게 overriden된 함수들은 무시된다. 이런 상황은 supertype들이 같은 조상을 공유할때 생긴다.
  • 두개 이상의 독립적으로 정의된 default 함수들이 충돌하거나 default 함수와 abstract method가 충돌하면 자바 컴파일러는 컴파일 에러를 생성한다. 이 경우 반드시 명시적으로 supertype 함수를 override 해야한다.

  • implement한 여러 인터페이스가 같은 시그니처를 가진 default 함수를 가진 경우 super 키워드를 사용해 호출할 수 있다.

  • public class FlyingCar implements OperateCar, FlyCar {
        // ...
        public int startEngine(EncryptedKey key) {
            FlyCar.super.startEngine(key);
            OperateCar.super.startEngine(key);
        }
    }
  • super 전에 오는 이름은 직접 호출된 함수를 정의하거나 상속받은 superinterface를 직접적으로 참조해야한다.

  • 이러한 형태의 함수 호출은 같은 시그니처의 default 함수들을 가진 다중 구현된 인터페이스들을 구분짓는데에만 적용되지 않는다.

  • super 키워드를 클래스와 인터페이스에 존재하는 default 함수를 호출할 경우에도 사용할 수 있다.

  • 클래스로부터 상속된 instance 함수들은 인터페이스의 abstract 함수들을 override할 수 있다.

Modifiers

  • overriding 함수는 overriden된 함수의 제어자보다 넓은 범위의 제어자만 사용할 수 있다.
  • 예를 들어 superclass의 instance 함수가 protected라면 subclass의 override instance 함수는 public은 될 수 있지만 private은 될 수 없다.
  • superclass의 instance 함수를 subclass에서 static 함수로 변경하려하거나 static을 instance로 변경하려하면 컴파일타임 에러를 받게된다.

Polymorphism

  • Polymorphism(다형성)의 사전적 정의는 종이나 개체가 많은 다른 형태나 상태를 가질 수 있다는 생물학의 원리이다.

  • 이 원리는 자바같은 객체지향 프로그래밍 및 언어에도 적용할 수 있다.

  • 어떤 클래스의 subclass들은 그들의 고유한 행동을 정의하면서 몇개의 부모클래스와 같은 기능성을 가질 수 있다.

  • public class TestBikes {
      public static void main(String[] args){
        Bicycle bike01, bike02, bike03;
    
        bike01 = new Bicycle(20, 10, 1);
        bike02 = new MountainBike(20, 10, 5, "Dual");
        bike03 = new RoadBike(40, 20, 8, 23);
    
        bike01.printDescription();
        bike02.printDescription();
        bike03.printDescription();
      }
    }
    • The Java virtual machine (JVM) calls the appropriate method for the object that is referred to in each variable. It does not call the method that is defined by the variable's type. This behavior is referred to as virtual method invocation and demonstrates an aspect of the important polymorphism features in the Java language.
    • JVM은 각각의 변수가 참조한 객체에게 적절한 함수를 호출한다.
    • 이 때 변수의 타입으로 정의된 함수를 호출하지 않는다.
    • 이러한 행동을 virtual method invocation / 동적 바인딩 이라고 부르고, 자바 언어의 중요한 다형성의 특징을 보여준다.

Hiding Fields

  • 클래스 안에서 superclass의 필드와 같은 이름을 가진 필드는 타입이 다르더라도 superclass의 필드를 숨긴다.
  • subclass 안에서 superclass의 필드는 name으로만 접근 할 수 없고 super키워드를 통해 접근해야한다.
  • 보통 field를 숨기는 것은 코드 가독성을 해치므로 추천하지 않는다.

Using the Keyword super

Accessing Superclass Members

  • If your method overrides one of its superclass's methods, you can invoke the overridden method through the use of the keyword super. You can also use super to refer to a hidden field (although hiding fields is discouraged)
  • 만약 어떤 함수가 superclass의 함수를 override 할 경우 overrideen된 함수를 super 키워드를 통해 호출할 수 있다.

Subclass Constructors

  • Invocation of a superclass constructor must be the first line in the subclass constructor.

  • superclass 생성자 호출은 언제나 subclass 생성자의 첫 줄에 와야한다.

  • If a constructor does not explicitly invoke a superclass constructor, the Java compiler automatically inserts a call to the no-argument constructor of the superclass. If the super class does not have a no-argument constructor, you will get a compile-time error. Object does have such a constructor, so if Object is the only superclass, there is no problem.

  • 만약 생성자가 superclass 생성자를 명시적으로 호출하지 않으면, 자바 컴파일러가 superclass의 아규먼트가 없는 생성자 호출을 자동적으로 삽입한다.

  • 만약 superclass가 아규먼트없는 생성자를 가지지 않는다면 컴파일타임 에러를 받는다. Object는 그런 생성자를 가지므로 Object만 상속받은 클래스인 경우 문제 없다.

  • 만약 subclass 생성자가 superclass의 생성자를 명시적으로든 암시적으로든 호출한다면 Object 생성자까지 올라가면서 연속적으로 생성자 호출이 일어남을 알 수 있다.

  • 이것을 constructor chaining이라고 한다.

Object as a Superclass

  • java.lang 패키지에 존재하는 Object 클래스는 class 계층에서 꼭대기에 존재한다. 모든 클래스는 직접/간접적으로 Object의 자손이다. 그러므로 Object의 instance 함수들을 상속받는다.
  • 만약 상속받은 instance 함수를 사용하려면 자신의 클래스에 맞게 override가 필요할 것이다.

The clone() Method

  • 만약 클래스 또는 클래스의 superclass중에 하나가 Cloneable 인터페이스를 구현한다면 clone 함수를 통해 존재하는 객체의 사본을 생성할 수 있다.

  • 사본을 생성하기 위해선 aCloneableObject.clone()를 사용해야 한다.

    • Object의 clone 함수의 구현은 clone함수를 호출한 객체가 Cloneable 인터페이스를 구현했는지 확인한다.
    • 구현x시 예외 throw
    • 클래스에서 clone함수 override 하려고 한다면
      public/protected Object clone() throws CloneNotSupportedException 형식으로 작성해야한다.
  • 만약 clone함수가 호출된 객체가 Cloneable 인터페이스를 구현했다면 객체의 clone함수 구현이 새로운 객체를 생성하는데, 이 객체는 원본 객체와 클래스가 같고 원본 객체의 맴버 변수들과 같은 값으로 맴버들이 초기화 된다.

  • class를 cloneable 하는 가장 쉬운 방법은 class declaration에 Cloneable을 implement하는 것이다.

  • 몇몇의 클래스들에게는 Object 클래스의 clone함수을 사용해도 올바르게 동작한다. 그러나 만약 객체가 외부의 객체를 참조하는 레퍼런스를 포함하고 있다면 clone함수를 override해 옮바른 방법으로 동작하게 구현해야한다.

  • 그렇지 않을 경우 원본 객체와 clone객체가 독립적으로 외부 객체를 가지지않는다.

  • clone함수를 override하여 원본 객체는 원본 외부 객체를 참조하고, clone 객체는 clone 외부 객체를 참조하도록 해 독립성을 만족시켜야한다.

The equals() Method

  • The equals() method compares two objects for equality and returns true if they are equal. The equals() method provided in the Object class uses the identity operator (==) to determine whether two objects are equal. For primitive data types, this gives the correct result. For objects, however, it does not. The equals() method provided by Object tests whether the object references are equal—that is, if the objects compared are the exact same object.
  • equals 함수는 두 객체의 동일성을 비교하고 true/false를 return 한다.
  • Object 클래스의 equals 함수는 ==를 통해 비교해 구현한다.
    • primitive data 타입은 옳바른 결과를 반환한다.
    • object의 경우는 아니다.
    • Object 클래스의 equals 함수는 객체의 주소값이 같은지를 비교한다.(동일한 객체인지 비교)
    • 두 객체가 같은 정보를 가졌는지 비교하려면 equals함수를 override해야한다.
    • equals함수를 override한다면 hashcode함수도 해야한다.

The finalize() Method

  • Object 클래스는 객체가 gc에 의해 제거됬을때 호출되는callback 함수인 finalize 함수를 지원한다.

  • Object 클래스의 finalize 함수 구현은 아무것도 하지 않는다. override해 자원을 free하는 cleanup작업으로 override할 수 있다.

  • finalize 메소드는 시스템에 의해 자동으로 호출 될 수 있지만 호출 될 지 안 될 지 불확실하다.

  • 그러므로 이 함수가 cleanup을 해주길 바라면 안된다.

  • 예를 들어 파일 디스크립터를 닫지않고 finalzie 함수가 대신 close하길 기대하면 파일 디스크럽트 공간이 부족해질 수 있다.

The getClass() Method

You cannot override getClass.

  • getClass 함수를 override 할 수 없다.
  • 클래스 객체를 반환한다.
    • 클래스 객체를 통해 클래스의 정보를 얻을 수 있다.
    • getSimpleName(), getSuperclass(), getInterfaces() 등
  • java.lang 패키지에 존재하는 Class 클래스는 50개가 넘는 함수를 가지고 있다.
    • isAnnotation(), isInterface(), getFields() etc..

The hashCode() Method

  • 객체의 메모리 주소를 16진수로 표현한 해쉬 코드 값을 반환한다.
  • 2개의 객체가 동일하다면 그들의 해쉬 코드도 반드시 동일해야한다는 것이 동일함의 정의다.
  • 만약 equals 함수를 override했다면 객체의 동일성 정의를 변경한 것이므로 Object 클래스의 hashCode 함수는 더 이상 유효하지 않다.
  • 그러므로 equal 함수를 override했다면 반드시 hashCode또한 override해야한다.

The toString() Method

  • 항상 toString 함수를 override 하는 것을 염두해야 한다.

  • Object의 toString 함수는 String으로 표현한 객체를 반환한다.

  • 디버깅에 매우 효과적

  • String으로 표현한 객체는 객체에 전적으로 달려있어 override해야한다.

Writing Final Classes and Methods

  • 클래스의 모든/몇몇의 함수들을 final로 선언할 수 있다.
  • final 키워드를 함수에 사용함으로써 subclass로부터 override될 수 없음을 표시한다.
  • Object의 몇몇 함수들은 final이다.
  • 객체의 상태유지가 중요하고 구현이 변경되면 안될 경우 함수를 final로 만든다.
  • 생성자로부터 호출되는 함수는 보통 final로 선언되어야 한다.
    • 만약 생성자가 final이 아닌 함수를 호출 시 subclass가 그 함수를 재정의해 원치 않은 결과를 낳을 수 있다.
  • final로 선언한 클래스를 상속받을 수 없다.
  • String 클래스같은 immutable 클래스를 생성 시 유용하다.

Abstract Methods and Classes

  • 추상 클래스는 abstract로 선언된 클래스이다.
  • abstract 함수를 포함하지 않을 수도 있다.
  • 인스턴스화 할 수 없지만, 추상 클래스를 상속받을 수 있다.
  • 추상 함수란 구현없이 함수선언부만 있는 함수를 말한다.
  • 만약 어떤 클래스가 추상 함수를 포함한다면 그 클래스는 반드시 abstract로 선언되어야한다.
  • 보통의 경우 추상 함수를 상속받은 subclass는 부모의 abstract 함수를 구현한다.
    • 구현하지 않으면 subclass 역시 abstract로 선언되어야한다.

Abstract Classes Compared to Interfaces

  • 추상 클래스는 인터페이스와 비슷하다.

  • 인스턴스화 할 수 없고, 구현이 된 함수와 안된 함수를 포함하고 있다.

  • 추상 클래스

    • final static이 아닌 필드를 선언할 수 있다.
    • private~public 함수를 정의할 수 있다.
  • 인터페이스

    • 필드는 무조건 public static final
    • 함수는 항상 public
  • 클래스는 1개만 extend 가능하지만 인터페이스는 여러개 implement 가능

  • 어떤 것을 사용해야 할까?

  • 추상 클래스 사용을 고려해야할 경우들

    • 여러 관련된 클래스들의 코드를 공유하고 싶을 때
    • 추상 클래스를 상속받는 클래스들이 공통의 필드와 함수들을 갖을 것을 기대하는 경우
    • 객체들이 각자의 필드를 갖길 원하는 경우
  • 인터페이스 사용을 고려해야할 경우들

    • 관련없는 클래스들이 너의 인터페이스를 구현할 것을 기대할 경우 ( ex)Comparable, Cloneable )
    • 데이터 타입의 행동을 특정짓고 싶은데 어떤 클래스가 구현하는지는 상관없는 경우
    • 타입의 다중 상속의 장점을 원하는 경우
  • AbstractMap 클래스가 추상 클래스의 예이다.

    • Collection FrameWork중 하나
    • subclass들(HashMap, TreeMap, ConcurrentHashMap)들은 AbstracMap이 정의한 많은 함수(get,put,isEmpty...)들을 공유한다.
  • HashMap 클래스는 여러 인터페이스를 구현한다.

    • Serializable, Cloneable, and Map<K, V>을 implements 함
    • 구현한 인터페이스들을 보아 해쉬맵의 인스턴스는 clone될 수 있고, serializable하며 map의 기능을 한다는 것을 알 수 있다.

An Abstract Class Example

  • 객체지향 그림 어플에서 원,삼각형,선 등의 많은 그래픽 객체를 그릴 수 있다.
  • 이런 객체들은 상태(위치,라인색,채운 색)와 행동(움직이기,돌리기,사이즈변경,그리기)을 공통으로 가진다.
  • 몇몇의 상태와 행동들은 모든 객체들에게 같다.(포지션, 채워진 색, 움직이기 등)
  • 몇몇은 다르게 구현해야한다(크기설정, 그리기)
  • 모든 크래픽 객체들은 그리기와 resize를 할 수 있어야하는데 각각 어떻게 하는지 다르다.
  • 이 예제가 추상 superclass를 같는 경우의 알맞다.
  • 일단 GraphicObject라는 추상 클래스를 선언해 subclass들이 공유하는 필드와 함수를 제공한다. 예를들어 현재 포지션을 return하는 함수나 moveTo 함수
  • draw 와 resize는 abstract 함수로 선언해 모든 subclass들이 각자 다르게 구현하게 한다.

When an Abstract Class Implements an Interface

  • 인터페이스 부분에서 인터페이스를 구현하는 모든 클래스는 무조건 인터페이스의 함수를 모두 구현해야한다고 했다.
  • 하지만 클래스를 abstract로 선언하면 모든 인터페이스의 함수를 구현하지 않아도 된다.
profile
안녕하세요

0개의 댓글