자바 상속의 특징
상속
이미 존재하는 클래스를 기반으로 새 클래스를 만드는 방법.
이미 존재하는 클래스를 나타내는 용어 | 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
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();
}
}
다이나믹 디스패치, 더블 디스패치
추상클래스
접근 제어자 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 어떻게 했어?! )
Object 클래스
자바의 모든 클래스는 Object 클래스를 상속한다.
오브젝트 클래스를 상속받기때문에 Object의 메서드도 같이 사용할 수 있다.
docs.oracle.com/javase/7/docs/api/java/lang/Object.html
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 |