점프투자바

SUADI·2022년 5월 3일

6. 객체지향 프로그래밍

(5) 상속

ㄱ) 상속

자바에서는 자식 클래스가 부모 클래스의 기능들을 물려받을 수 있는 상속(inheritance) 기능이 있다.

class Animal {
	String name;
    
	void setName(String name) {
    	this.name = name;
    }   
}

class Dog extends Animal {
} 

public class Sample {
	public static void main(String[] args) {
    	Dog dog = new Dog();
        dog.setName("Puppy");
        System.out.println(dog.name); // Puppy 출력
    }
}
  • extends라는 키워드를 사용해서 부모 클래스로부터 메소드, 객체변수 등을 상속받을 수 있다. 자식 클래스는 setName 메소드를 선언을 하지 않고도 사용할 수 있다.

ㄴ) 자식 클래스의 기능을 확장

class Animal {
	String name;
    
	void setName(String name) {
    	this.name = name;
    }   
}

class Dog extends Animal {
	void sleep() {
    	System.out.println(this.name + " zzz");
    }
} 

public class Sample {
	public static void main(String[] args) {
    	Dog dog = new Dog();
        dog.setName("Puppy");
        System.out.println(dog.name); // Puppy 출력
        dog.sleep(); // Puppy zzz 출력
    }
}
  • 자식 클래스는 부모 클래스로부터 상속 받은 기능 외에 기능을 더 추가할 수 있다.

ㄷ) IS - A 관계

A is a B 관계이다. B가 상위개념, A가 하위개념이다. dog is a Animal. 이렇게 IS-A 관계에 있을 떄 자식 클래스의 객체는 부모 클래스의 자료형인 것처럼 사용할 수 있다.
[이해안가는 부분]
자식 클래스의 객체를 부모 클래스의 자료형으로 사용하는 목적을 잘 모르겠다.

Animal dog = new Dog(); 
// Dog 클래스의 객체를 Animal 자료형으로 사용 
// Dog is a Animal (o)
  • Dog 객체를 Animal 자료형으로 사용할 경우, Dog클래스에만 존재하는 기능(sleep 메소드) 사용하지 못한다. Animal 클래스의 기능만 사용 가능하다.(setName 메소드)
Dog dog = new Animal();
// Animal 객체를 Dog 자료형으로 사용 불가.
// 동물로 만든 객체는 개도 될 수 있고, 호랑이도 될 수 있고..
// Animal is a Dog. (x) 컴파일 오류!!
  • 부모 클래스에서 나온 객체는 자식 클래스의 자료형으로 사용할 수 없다.

[이해안가는 부분]
아직까지 Object 클래스의 역할이 뭔지는 잘 모르겠다.

  • 자바의 모든 클래스는 Object 클래스를 상속받는다.
class Animal extends Object {
}

굳이 extends Object를 하지 않아도 자동적으로 상속받게끔 되어 있다.

ㄹ) 메소드 오버라이딩(Method Overriding)

Dog 클래스를 더 세분화해서 집에서 기르는 개인 HouseDog 클래스를 만들어서 추가적으로 메소드를 만든다고 해보자.


class Dog extends Animal {
    void sleep() {
        System.out.println(this.name+" zzz");
    }
}

class HouseDog extends Dog {
	void sleep() {
    	System.out.println(this.name + "zzz in house");
    }
}

public class Sample {
    public static void main(String[] args) {
        HouseDog houseDog = new HouseDog();
        houseDog.setName("happy");
        houseDog.sleep();  // happy zzz in house 출력
    }
}
  • HouseDog 클래스는 Animal 클래스의 상속을 받은 Dog 클래스를 상속받았기 때문에 Animal 클래스와 Dog 클래스의 기능을 다 물려받아서 setName 메소드와 slepp메소드를 모두 사용할 수 있다.

  • Dog 클래스의 sleep메소드와는 다르게 HouseDog 클래스에서는 sleep 메소드를 수정하여 사용하고 싶을 때 메소드 오버라이딩으로 sleep 메소드를 덮어써서 Dog 클래스의 sleep 메소드보다 HouseDog 클래스의 sleep 메소드가 우선순위를 가지도록 할 수 있다.

  • 메소드 오버라이딩을 하는 방법은 부모 클래스 내의 메소드 중 수정해서 사용하고자 하는 메소드와 똑같은 이름의 메소드를 자식 클래스 내에 만들고 수정하고자 하는 뱡향대로 코드를 작성하면 된다.

ㅁ) 메소드 오버로딩(Method Overloading)

메소드 오버라이딩은 메소드를 수정하는 용도라면 메소드 오버로딩은 메소드를 새로 추가하는 용도이다.

...
class HouseDog {
	void sleep() {
		System.out.println(this.name + " zzz in house");     	
    }
    
	void sleep(int hour) {
		System.out.println(this.name + " zzz in house for " + hour + " hours");    
    }
}

public class Sample {
	public static void main(String[] args) {
    	HouseDog housedog = new HouseDog();
        housedog.setName("Happy");
        housedog.sleep(); // Happy zzz in house
        housedog.sleep(3); // Happy zzz in house for 3 hours 출력
    }
}

메소드 오버로딩은 입력값이 달라져서 결과적으로 이름만 같은 새로운 메소드가 추가된다는 점에서 메소드 오버라이딩과 다르다.

ㅂ) 다중상속

자바는 다른 언어들과 다르게 다중상속을 지원하지 않는다. 다중상속이란 하나의 클래스가 여러개의 클래스를 상속받는다는 의미이다. 자바에서 다중상속이 안되는 이유는 여러개의 클래스 중 어떤 클래스를 먼저 실행해야 할지 우선순위에 관한 문제 등 여러 모호한 상황을 없애고자 지원하지 않는다.

class A {
}
class B {
}
class C extends A,b { // 컴파일 오류!
}

(6) 생성자(Constructor)

ㄱ) 생성자

메소드명이 클래스명과 동일하고 리턴 자료형을 정의하지 않은 메소드를 생성자라고 한다. 생성자의 용도는 예를 들어 HouseDog 클래스의 객체를 생성할 때 생성자를 사용하지 않았을 때는 setName 메소드를 이용하여 이름을 지어주지 않았을 때 이름을 출력하면 null값이 나왔었다. 객체를 생성할때 꼭 setName 메소드를 이용하여 이름을 짓도록 강제하고 싶을때 생성자를 사용하면 된다. 생성자를 사용하면 객체를 생성할 때 괄호 안의 입력칸에 이름을 지어야 오류가 나지 않는다. 생성자를 만드는 규칙은

  • 클래스명 = 메소드명
  • 리턴 자료형 X (void도 사용안함)
...

class HouseDog extend Dog {
	HouseDog(String name) { // 생성자
    	this.setName(name);
    }	
}
public class Sample {
	public static void main(String[] args) {
    	HouseDog housedog = new HouseDog("Happy");
        System.out.println(housedog.name); // Happy 출력
    }
}

ㄴ) 디폴트 생성자(Default Constructor)

지금까지 생성자를 사용하지 않고 코드를 작성해왔는데 컴파일러가 자동으로 디폴트 생성자를 추가해왔기 때문에 정상적으로 작동한 것이다. 디폴트 생성자는 입력인자도 없고 내용도 없는 생성자를 의미한다.

...
class Dog extends Animal {
	Dog() { // 디폴트 생성자
    }
    ...
}
...
...
class Dog extends Animal {
	 ...
}
...

이 두 코드는 같은 결과를 도출해낸다. 생성자를 작성하지 않을 경우에는 컴파일러가 자동적으로 디폴트 생성자를 추가해 주지만 직접 생성자를 작성하면 생성자가 있기 때문에 생성자를 따로 추가해 주지 않는다. 이러한 이유 때문에 생성자를 작성했을 경우

HouseDog housedog = new HouseDog();

main 메소드에 다음과 같이 코드를 짰을때 디폴트 생성자를 따로 추가해주지 않기 때문에 컴파일 오류가 발생하는 것이다.

ㄷ) 생성자 오버로딩(Constructor Overloading)

메소드 오버로딩이 있듯이 생성자에도 오버로딩이 있다. 동일한 생성자명에 입력인자가 각각 다른 생성자를 여러개 생성할 수 있다.

...
class HouseDog extends Dog {
	HouseDog(String name) {
    	this.setName(name);
    }
    
    HouseDog(int type) {
    	if (type == 1) {
        	this.setName("Happy");
        } else (type == 2) {
        	this.setName("Hope");
        }
    }
}

public class Sample {
	public static void main(String[] args) {
    	HouseDog housedog1 = new HouseDog("Puppy");
		System.out.println(housedog1.name); // Puppy 출력
        
       	HouseDog housedog2 = new HouseDog(1);
		System.out.println(housedog2.name); // Happy 출력
        
        HouseDog housedog3 = new HouseDog(2);
		System.out.println(housedog3.name); // Hope 출력
	}
}

0개의 댓글