본문 바로가기
Learning-log/JAVA

[Java] 상속과 다형성

by why제곱 2023. 4. 1.

상속

  • 부모에서 private으로 막아놓은걸 자식이 오픈할 순 없음

새로운 설계도를 만들 때 맨땅에서 새로 시작하는게 아니라 기존에 만들어둔 class에 플러스해서 새로운 걸 만들 수 있지 않을까? ⇒ 상속이란, 기존의 설계도를 재사용하기 위해 등장

  • 상속 : 어떤 클래스의 특성을 그대로 갖는 새로운 클래스를 정의한 것
    • 기존 클래스 : 상위 클래스, 부모클래스, super class
    • 상속받은 클래스 : 하위 클래스, 자식클래스, sub class
    ⇒ 상속을 받은 자식 클래스를 호출하면 부모클래스를 먼저 호출. 부모클래스 도장 꽝 찍은 후 나머지 자식 클래스로도 도장 꽝!
  • ⇒ 그래서! 자식 클래스에서 부모클래스 내에 있던 메소드나 변수 볼 수 있는 거
  • 상속 안 받은 경우 : → 자동완성 창에 클래스 자기 자신의 이름이 뜸
  • 상속을 받은 경우 : 해당 변수나 메소드를 어떤 부모에게 상속 받았는지 자동완성에 뜸.
    • Object클래스는 상속 받은 적 없는데 자동완성 창에 왜 뜰까?
    • 별도의 extends 선언이 없는 클래스는 extends Object가 생략된 것이며 다른 클래스를 상속하더라도 그 상속된 클래스가 또 Object class를 상속함. 상속받은 부모 클래스가 Object클래스를 상속받아 그 자식 클래스도 Object class를 상속받게 되는 것. (꼬리물기 !!)
  • 확장성, 재사용성
    • 부모의 생성자와 초기화 블록은 상속받지 않음
    • ⇒ 도장을 찍은 것만 상속 받는 것.
    • 멤버변수나 메서드만 상속 받음
  • 클래스 선언 시, extends 키워드 명시
    • Java는 다중 상속 허용X, 단일 상속 지원
    • extends 다음에 여러 클래스가 나오면 안 됨.
    • 두 클래스를 상속 받는다면? 두 클래스에 같은 이름의 변수나 메서드가 있을 때 두 변수나 메서드 사이의 충돌이 되므로 안 됨!
  • 자식 클래스는 부모 클래스의 멤버변수, 메소드를 자신의 것처럼 사용할 수 있다. 단 접근제한자에 따라 사용여부가 달라진다.
    • private : 상속 불가
    • (default) : 같은 패키지 내라면 상속 가능
    • protected : 같은 패키지에 있어도, 다른패키지에 있더라도 상속관계가 되면 사용 가능.
    • public : 가능

 

super (this와 비슷하다 !)

해당하는 위치로 가서 가장 가까운 변수랑 메소드를 찾아라!!!

  • super 키워드 : 조상 클래스의 생성자 호출
  • 부모 클래스의 멤버변수나 메서드를 찾아가는 역할
    • this는 객체 자기 자신의 주소를 가리키는 것과 유사하게 super는 자신이 상속한 class가 찍어낸 객체의 주소를 가리키게 됨.
    • ⇒(부가설명 : 상속받은 class가 객체를 찍어내면 부모 class를 먼저 찍어낸 후 자식class가 찍힘. this는 여기서 자식class가 찍은 부분의 주소를 가리키게 되고, super는 부모class가 찍어낸 부분의 주소를 가리키게 됨.)
    • super와 this가 있으면 이 둘이 가리키는 주소에서 가장 가까이에 있는 그 변수, 메소드를 찾아가 참조하는 것.
    • this는 자기 객체 내, 없으면 그 바깥/ super는 부모 class가 찍은 객체 부분
  • 생성자로 멤버변수 초기화 할 때
    • 자기 자신의 멤버변수를 초기화하고 싶을 때 this를 사용하듯이 부모에게 물려받은 변수를 초기화할 때는 super 사용.

오버라이딩(재정의, overriding) : 메서드

  • 상위 클래스의 메서드를 상속받은 후, 재정의해서 사용하는 것.(재정의)
  • 메서드의 이름, 반환형, 매개변수(타입, 개수, 순서) 동일해야함
  • 하위 클래스의 접근제어자 범위가 상위클래스보다 크거나 같아야한다.
  • 조상보다 더 큰 예외를 던질 순 없다.
    • 부모 클래스가 메서드에서 예외를 던질 수 있는데 그보다 더 높은 수준의 예외를 던질 수 없음.
  • 어노테이션 : 컴파일러가 있는 주석
    • 컴파일러에게 부모클래스에 있는 메서드를 오버라이드 하려함을 알려서 실수 등으로 인해 부모클래스에 없는 메서드를 오버라이딩하게 되는 사고를 예방 가능. 사용 권장!

메서드 오버로딩

한 클래스에서 같은 이름의 메서드가 파라미터를 달리하여 만들 수 있음.

오버라이딩

같은 메서드를 자식 클래스가 재정의하여 사용하는 것.

 

Object 클래스

  • 가장 최상위 클래스로 모든 클래스의 조상
  • Object의 멤버는 모든 클래스의 멤버
  • equals()
    • 두 객체가 같은지 비교하는 메서드.
    • Object class에서는 주소값을 비교하는 메서드.
    • String에서는 실제 문자열 값을 비교하는 것으로 재정의해서 쓰고 있음.
  • hashCode
    • 시스템에서 객체를 구별하기 위해 사용되는 정수값
    • 특징
      • 2개의 다른 해시값 ⇒ 두 객체가 동일하지 않음.
      • 2개가 같은 해시값 ⇒ 두 객체가 동일할 수도, 아닐 수도 있음.
    • HashSet, HashMap 등에서 객체의 동일성을 확인하기 위해 사용
    • equals 메서드를 재정의할 때는 hash코드도 재정의할 것.
    • ⇒ equals()가 같을 때 hashCode()도 같은 값을 리턴해야 하므로 -! (규칙)
    • 객체를 비교할 때 드는 비용을 낮추기 위해 사용.
    • 두 객체를 비교할 때 hashCode를 비교하면 절대 같을 수 없는 경우를 빠른 연산으로 확인 가능. hashCode가 같으면 그 때, equals()을 사용하여 두 객체 같은지 확인

equals와 hashCode함수의 오버라이딩에 관련해서는 아래 글을 참고하며 추가로 공부했다.

https://mangkyu.tistory.com/101

 

[Java] equals와 hashCode 함수

1. equals와 hashCode란? equals와 hashCode는 모든 Java 객체의 부모 객체인 Object 클래스에 정의되어 있다. 그렇기 때문에 Java의 모든 객체는 Object 클래스에 정의된 equals와 hashCode 함수를 상속받고 있다. [ e

mangkyu.tistory.com

 

해시(Hash)

데이터를 다루는 기법 중 하나.

  • 해시 함수 : 데이터를 효율적으로 관리하기 위해 임의의 길이의 데이터를 고정된 길이의 데이터로 매핑하는 함수 ( 매핑하는 과정 : 해싱(Hashing))
String name = "이름";

//String이라는 타입으로 인해 stack에 메모리 공간 확보하고 그 공간에 name이라 이름 붙임.
//Hash 공간에 "이름"이라는 데이터 생성(임의의 길이의 데이터)
고정된 길이의 데이터인 name과 Hash의 "이름"을 Mapping
  • 해시 테이블(Hash Table) : key와 value가 하나의 쌍을 이루는 데이터 구조.
    • key를 해시 함수로 계산하여 그 값을 배열의 인덱스로 사용.
    • 해시코드(HashCode) : 해시 함수에 의해 반환된 데이터의 고유 숫자 값. 이 값을 배열의 index로 사용 → 해당하는 Index에 data 넣기
    • ⇒ 이런 식으로 데이터를 저장하다보면 인덱스 값 중복 가능. (충돌) → 충돌이 적은 hash함수 사용하는 게 좋음.
    • 장점 : 적은 리소스로 많은 데이터 관리, 검색, 삽입, 삭제가 빠름.

 

final

  • 해당 선언의 최종상태, 결코 수정 불가
  • final class 수정금지
  • final 메서드 : overriding 금지
  • final 변수 : 더 이상 값을 바꿀 수 없음 상수화
    • 수정될 수 없기 때문에 초기화 필수
    • 객체 내 변수라면 생성자, static블럭을 통한 초기화까지는 허용.
  • final 클래스 : 다른 클래스가 상속할 수 없는 클래스
  • static + final : 그 값을 계!속! 쓴다는 뜻. 어차피 같은 값을 쓸 경우, 변수가 객체마다 생성돼서 메모리 차지하지 말고 static으로 쓴다면 효율성이 높아짐.

 

다형성

  • 상속관계에 있을 때, 조상 클래스의 타입으로 자식 클래스 객체를 참조할 수 있다.
  • (부모클래스) 객체명 = new (자식클래스) ;
  • ⇒ 부모클래스의 필터 안경을 쓰고 자식클래스가 찍어낸 객체를 바라보자. 부모클래스에 없는 변수나 메서드는 볼 수 없으며, 부모클래스에 있는 변수의 경우 인지할 수 있으므로 자식클래스가 정의한 데이터값으로 그 변수를 인지한다. (super로 가리킨 경우는 부모의 값을 가져오겠지?!)
  • 활용
    • 매개변수의 다형성메서드가 호출되기 위해서는 메서드 이름과 파라미터가 맞아야 한다.
      • 코드로 살펴보는 매개변수의 다형성
      //기존 코드
      class Product {
      	int price;
      	int point;
      }
      class Tv extends Product {}
      class Computer extends Product {}
      
      class Buyer{
      	int money = 100000;
      	int myPoint = 0;
      	void buy(Tv t) {
      		money -= t.price;
      		myPoint += t.point;
      
      //필드 다형성 
      Product p1 = new TV();
      Product p2 = new Computer();
      Product p3 = new Phone();
      
      //매개변수 다형성
      void buy(Product p) {
      	money -= p.price;
      	myPoint += p.point;
      }
      
    • 참조형 매개변수는 메서드 호출시 자신과 같은 타입이나 자손타입의 인스턴스를 넘겨줄 수 있음
    위의 첫번째 코드에서의 Buyer class의 buy 메서드로는 Tv만 살 수 있다. 다른 제품을 구매하는 메서드를 사용하기 위해서는 해당 제품을 매개변수로 갖는 메서드를 또 정의해야하는 번거로움이 있다. 하지만 참조변수를 Product로 하고, TV,Computer,Phone(Product를 상속받는 class) class들을 buy 메서드의 매개변수로 사용하여 여러 메서드를 정의하지 않고 활용할 수 있다. 이를 매개변수의 다형성이라 한다.
    • 배열에서의 활용 : 같은 배열 내에 여러가지 타입의 데이터를 넣을 수 있음
      • 일반 변수 타입을 배열 선언할 때 하듯이 class를 변수 타입으로 설정하고 이 class를 상속받은 class들을 배열 요소로 넣을 수 있음
      Public static void main(String[] args) {
      	Product[] itemList = new Product[3]
      
      	itmeList[0] = new Tv();
      	itemList[1] = new Computer();
      	itemList[2] = new Phone();
      
      ex. Tv, Computer, Phone은 Product 내의 값은 아니지만 이 세 클래스는 Product를 상속하므로 Product를 변수로 갖는 itemList에 들어가 수 있음.
  • 참조를 하게되면 제약사항 발생 : Student로 만들어도 Person의 관점(필터)으로 보기 때문에 Person에는 없고 Student에만 있는 속성들이 보이지가 않아.
  • 형변환
    • 작은집(우변)에서 큰집(좌변) : 묵시적 캐스팅 / 즉, 부모 타입 = new 자식 클래스 는 묵시적
    • 큰집에서 작은집 : 명시적 캐스팅 / 자식 타입 = new 부모 클래스 는 명시(잘 안 씀 ,, )⇒ 조상을 무작정 자손으로 바꿀 수 없음. instanceOf연산자를 통해 한번 체크 후 형변환 가능
    • ⇒ (super 일수록 큰 것.)
  • instanceOf 연산자 : 객체가 어떤 클래스이며 어떤 클래스를 상속받았는지 확인하는 데 사용
  • ⇒ A instanceOf B : A가 B class이거나 B를 상속받는 클래스라면 true 리턴(리턴값 boolean)