본문 바로가기
JAVA

상속

by e-pd 2020. 12. 21.

https://github.com/whiteship/live-study/issues/6

 

자바 상속의 특징


 

상속

이미 존재하는 클래스를 기반으로 새 클래스를 만드는 방법.

 

이미 존재하는 클래스를 나타내는 용어 parent class, base class
새 클래스를 나타내는 용어 child class, derived class

 

 

 

 

 

 

두 클래스 간의 상속의 표현

  • 자식 클래스가 부모 클래스를 상속받음
  • 자식 클래스가 부모 클래스로부터 파생
  • 자식 클래스가 부모 클래스의 한 종류(is-a)

is-a 관계 : 상속관계, 부분집합 관계

 

자바에서 클래스 상속을 이용할때는 extends 키워드를 사용하여 상속 관계를 나타낸다.

 

 

 

 

 

 

부모와 자식이 있을 경우 부모부터 초기화된다

public class Animal {
    private String name;
    private int age;

    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("parent");
    }
}


public class Dog extends Animal {
    public Dog(String name, int age) {
        super(name, age);
        System.out.println("child");
    }
}

1. 메모리에 객체 생성

2. 부모 생성자 호출

3. 자식 생성자 호출

public class A {}
public class B extends A {}
public class C extends C {}

// C를 생성하였을때 A -> B -> C 순으로 생성자를 호출한다.

 

 

컴파일러가 부모 생성자 중 하나를 알아서 호출

public class Animal {
}

public class Dog extends Animal {
}

 

Dog는 Animal를 호출 하지않았지만 컴파일러가 내부적으로 Animal 생성자를 호출한다.

 

 

 

 

 

다중 상속

자바는 다중 상속을 지원하지 않는다. 

 

 

 

 

부모의 멤버변수,함수 접근

public class Animal {
    private String name;
    private int age;

    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}

public class Dog extends Animal {

    public Dog(String name, int age) {
        super(name, age);
    }

    public void setAge(int age) {
        this.age = age;
    }
}

위의 클래스에서 Dog클래스는 부모 클래스를 상속받았지만 부모 멤버 변수를 접근하여 사용할 수 없다. 

왜냐하면 접근제어자가 private이기 때문이다. 

 

 

수정을 위해서는 상속에 사용할 변수나 함수를 protected를 변경하여 사용한다.

 

protected 자료형 변수명;

protected 반환형 함수명(매개변수) {
	...
}

protected class 클래스명 {
	...
}

protected를 사용하면 같은 패키지에 속한 클래스나 자식 클래스만 접근 가능하다.

 

 

부모에서 자식으로 타입변환은 안전하지만

자식에서 부모는 컴파일러가 오류를 발생시킨다.

 

public static void main(String[] args)  {
        Dog dog = new Dog("puppy", 2);
        Animal animal = dog;
        
        Dog dog2 = animal;  // compile error
    }

 

 

 

 

부모가 동일해도 형제끼리는 캐스팅 할 수 없다.

public class Animal {
}

public class Dog extends Animal {
}

public class Cat extends Animal{
}

public static void main(String[] args)  {
        Dog dog = new Dog();
        Animal animal = dog;
        Cat cat = dog;      // error
}

 

 

 

 

컴파일 타임에 상속관계 오류를 찾아내기도 하지만 못찾는 경우도 있다

    public static void main(String[] args)  {
        Animal animal = new Cat("kitty", 2);
        Dog dog = (Dog) animal;
    }
Exception in thread "main" java.lang.ClassCastException: 

 

 

 

 

상속의 장단점 

장점

재사용성

코드 중복 줄어든다

관련 코드를 한 파일로 관리할 수 있다

 

단점 

상속 단계가 증가하면 추상화하기 힘들다

잘못된 상속

  • Vector 클래스의 자식 클래스로 구현한 stack
  • InstrumentedHashSet

jaehun2841.github.io/2020/07/05/object-chapter10/#%EC%B0%A8%EC%9D%B4%EC%97%90-%EC%9D%98%ED%95%9C-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D

 

Objects Study - Chapter10. 유연한 설계 | Carrey`s 기술블로그

상속 객체지향 프로그래밍의 장점 중 하나는 코드를 재사용하기가 용이하다는 점이다. 전통적인 패러다임에서는 코드를 복사 후 붙여넣기 하고 수정하여 코드를 재사용하였다. 객체지향에서는

jaehun2841.github.io


super 

 

부모 클래스 생성자 호출시

super(매개변수목록)

부모 클래스 멤버 변수/함수 호출시

super.부모의 맴버 변수
super.부모의 메서드

super는 현 객체의 부모.

super()는 부모 생성자 호출

 

상속관계가 만들어져도 부모는 여전히 생성 가능하다.

    public static void main(String[] args)  {
        Animal animal = new Animal();
        Dog dog = new Dog();
    }

 

 


Overriding

부모 클래스에서 정의된 메서드를 자식 클래스에서 같은 시그니처로 다시 정의 

 

같은 지시를 내렸는데 다른 종류의 동작을 하는 것

 

Late Binding

어떤 함수 구현이 실행될지는 실행 중에 결정(일반적인 함수는 컴파일 중에 결정)

 

 

오버라이딩 조건 

1. 부모 시그니처와 동일해야함

2. 접근 제어자를 더 작은 범위로 변경 할 수 없음

3. 자식 클래스에 부모 메서드보다 더 큰 범위 에러 선언 안됨

www.tcpschool.com/java/java_inheritance_overriding

 

public class Animal {
    private String name;
    private int age;

    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public void speak() {
        System.out.println("speak");
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}

public class Dog extends Animal {

    public Dog(String name, int age) {
        super(name, age);
    }

    @Override
    public void speak() {
        System.out.println("멍멍");
    }
}


public class Cat extends Animal{
    public Cat(String name, int age) {
        super(name, age);
    }

    @Override
    public void speak() {
        System.out.println("야옹");
    }
}

 

부모의 동작을 유지하면서 오버라이딩도 가능하다. 

 

public class Cat extends Animal{
    public Cat(String name, int age) {
        super(name, age);
    }

    @Override
    public void speak() {
        System.out.println("야옹");
        super.speak();
    }
}

 

 


 

다이나믹 메소드 디스패치 (Dynamic Method Dispatch)

프로그램이 어떤 메서드를 호출하여 결정하여 실행하는 과정

ko.wikipedia.org/wiki/%EB%8F%99%EC%A0%81_%EB%94%94%EC%8A%A4%ED%8C%A8%EC%B9%98

 

 

디스패치에는 static dispatch와 dynamic dispatch가 존재한다.

 

정적 디스패치

컴파일 시점에 어떤 메서드가 실행될지 알 수 있다. 

public class App {
    static class Programing {
        void printLanguage() {
            System.out.println("Java");
        }
    }

    public static void main(String[] args) {
        new Programing().printLanguage();
    } 
}

 

동적 디스패치

method가 오버라이드 되어있는 경우 컴파일 시점이 아닌 실행시점에서 어떤 메소드를 실행할 지 결정된다.

public class App {
    static abstract class Programing {
        void printLanguage() {
        }
    }
    static class ProgramingLanguage1 extends Programing {
        @Override
        void printLanguage() {
            System.out.println("Java");
        }
    }
    static class ProgramingLanguage2 extends Programing {
        @Override
        void printLanguage() {
            System.out.println("C++");
        }
    }

    public static void main(String[] args) {
        Programing programing = new ProgramingLanguage1();
        programing.printLanguage();
    }
}

 

다이나믹 디스패치, 더블 디스패치

 

youtu.be/s-tXAHub6vg

 

 

 

 


추상클래스

접근 제어자 abstract class 클래스명 {
	...
}

 

  • 인스턴스를 만들 수 없는 클래스 (인스턴스를 만들 수 있는 클레스는 구체 클래스)
  • 다른 클래스의 부모 클래스가 될 수 있다
  • 반드시 추상 메서드가 있을 필요는 없다

 

 

 


Final

 

클래스 앞에 붙는 final 더 이상 상속하지 못함

- 상속하지 못하게 되어 자식 클래스가 생길 수 없다

public final class Animal {
    private String name;
    private int age;
}

Cat 클래스에서 Animal을 상속받으려고 하면 에러발생

 

메서드 앞에 final을 붙이면 자식 클래스에서 사용할 수 없다.

 

public class Animal {
    private String name;
    private int age;

    public final void speak() {
        System.out.println("speak");
    }

 

 

 

변수 선언 앞부분에 Final을 사용하면 변수 재할당을 막는다.

 

 

 

 

final과 관련되어 더 생각하면 좋은 문제들

([Live] Java: 그가 final 도배 하는 이유 / 컴파일러 String 어떻게 했어?! )

youtu.be/lcPfxmn0otA

 

 


Object 클래스

 

자바의 모든 클래스는 Object 클래스를 상속한다. 

오브젝트 클래스를 상속받기때문에 Object의 메서드도 같이 사용할 수 있다. 

 

docs.oracle.com/javase/7/docs/api/java/lang/Object.html

 

Object (Java Platform SE 7 )

Called by the garbage collector on an object when garbage collection determines that there are no more references to the object. A subclass overrides the finalize method to dispose of system resources or to perform other cleanup. The general contract of fi

docs.oracle.com

Object내의 메서드


toString()

사람이 읽기 편하게 객체를 문자열로 표현

 

public class Animal {
    private String name;
    private int age;

    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void speak() {
        System.out.println("speak");
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    @Override
    public String toString() {
        return "Animal{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
    public static void main(String[] args) {
        System.out.println(new Animal("dog",2));
    }
Animal{name='dog', age=2}

 

 

equals()

클래스 속 데이터를 비교하려면 오버라이딩 필요하다 (단순 주소 비교는 '=='을 사용)

equal를 구현시 hashcode() 도 같이 오버라이딩해야한다.

 

  @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Animal)) return false;
        Animal animal = (Animal) o;
        return getAge() == animal.getAge() &&
                Objects.equals(getName(), animal.getName());
    }

 

 

hashcode() 메서드

객체를 나타내는 hash값을 32비트 정수로 변환

(만약 같은 객체라면 hash값이 동일하다)

객체 비교시 유용하게 사용

 

    @Override
    public int hashCode() {
        return Objects.hash(getName(), getAge());
    }

'JAVA' 카테고리의 다른 글

인터페이스  (0) 2021.01.03
패키지  (0) 2020.12.28
클래스  (0) 2020.12.15
Java 코딩의 기술-4  (0) 2020.12.06
Java 코딩의 기술-3  (0) 2020.12.05