클래스와 인스턴스에 대해 공부했으니 그 다음으론 상속(inheritance)
에 대해 알아보자.
class Cal{
public int sum(int v1, int v2) {
return v1+v2;
}
}
public class InheritanceApp {
public static void main(String[] args) {
Cal c=new Cal();
System.out.println(c.sum(2,1));
}
}
Cal
이라는 클래스를 더이상 수정할 수 없거나, 수정하고 싶지 않을 때를 생각해보자.
근데 저 클래스에 -
기능을 넣고 싶다고 생각하자!
가장 쉽게 생각할 수 있는 건 저 클래스 자체를 복사해서, minus
라는 새로운 기능을 추가하는 것이다.
class Cal{
public int sum(int v1, int v2) {
return v1+v2;
}
}
class Cal2{
public int sum(int v1, int v2) {
return v1+v2;
}
public int minus(int v1, int v2) {
return v1-v2;
}
}
public class InheritanceApp {
public static void main(String[] args) {
Cal c=new Cal();
System.out.println(c.sum(2,1));
}
}
이렇게 말이다! 근데 만약 Cal
클래스의 sum
메소드가 변경이 되었다고 가정해보자.
클래스를 복제했기 때문에 메소드가 변경된지도 모를 뿐더러, 복제한 클래스에도 다 변경사항을 적용해줘야 하는 불편한 상황이 생긴다.
그리고, Cal
의 메소드와 Cal2
의 메소드 내용이 같은 내용이라는 것을 파악하기도 쉽지 않다. (지금은 한줄이라서 쉽지만..)
이러한 상황에서 행복을 주는 것이 상속
이다!
Cal2
는 필요하지 않으니 뒤로 보내고, Cal
을 상속받은 클래스를 작성해보자.
class Cal3 extends Cal{
}
extends
를 클래스 이름 뒤에 쓰고, Cal3
는 이제, Cal
클래스를 확장해서 Cal
클래스가 가지고 있는 모든 메소드와 변수
를 상속받게 된다.
class Cal{
public int sum(int v1, int v2) {
return v1+v2;
}
}
class Cal3 extends Cal{
}
public class InheritanceApp {
public static void main(String[] args) {
Cal c=new Cal();
System.out.println(c.sum(2,1));
Cal3 c3=new Cal3();
System.out.println(c3.sum(2, 1));
}
}
class Cal2{
public int sum(int v1, int v2) {
return v1+v2;
}
public int minus(int v1, int v2) {
return v1-v2;
}
}
잘 동작하는 걸 확인할 수있다!
c3
가 가리키는 클래스에서 sum
이라고 하는 메소드를 찾는데, Cal
을 확장하고 있기 때문에 Cal 클래스에서 sum이라는 메소드를 찾는다.
이것이 바로 상속이당
위의 소스코드를 더 실용적이게 바꿔보자.
지금 Cal3
는 Cal
이 가지고 있는 기능을 그대로 가질 뿐 추가적인게 없다!
Cal
이 가지고 있던 기능에서 없던 기능(minus
)을 추가할 것이다.`
class Cal{
public int sum(int v1, int v2) {
return v1+v2;
}
}
class Cal3 extends Cal{
public int minus(int v1, int v2) {
return v1-v2;
}
}
public class InheritanceApp {
public static void main(String[] args) {
Cal c=new Cal();
System.out.println(c.sum(2,1));
Cal3 c3=new Cal3();
System.out.println(c3.sum(2, 1));
System.out.println(c3.minus(2, 1));
}
}
class Cal2{
public int sum(int v1, int v2) {
return v1+v2;
}
public int minus(int v1, int v2) {
return v1-v2;
}
}
실행시켜보면 잘 동작한다!
부모 클래스Cal
가 가진 sum
메소드가 마음에 안들었다고 가정해보자.
수정할 수 있다!!
class Cal{
public int sum(int v1, int v2) {
return v1+v2;
}
}
class Cal3 extends Cal{
public int sum(int v1, int v2) { //overriding
System.out.println("Cal3!!");
return v1+v2;
}
public int minus(int v1, int v2) {
return v1-v2;
}
}
public class InheritanceApp {
public static void main(String[] args) {
Cal c=new Cal();
System.out.println(c.sum(2,1));
Cal3 c3=new Cal3();
System.out.println(c3.sum(2, 1));
System.out.println(c3.minus(2, 1));
System.out.println(c3.sum(2, 1));
}
}
Cal
클래스의 sum이 아닌 Cal3
클래스의 sum으로 동작하는 것을 확인할 수 있다!
이것은, 부모가 가지고 있는 메소드를 재정의
한 것이며, 이것을 Overriding
이라고 한다.
Overriding
: 부모 클래스의 기능을 올라타서(덮어써서) 재정의 한 것.
Overloading
: 너무 많이 탑재하다. 과적하다. 기존에 존재하는 메소드와 같은 이름의 형태만 다른 메소드.
class Cal{
public int sum(int v1, int v2) {
return v1+v2;
}
public int sum(int v1, int v2, int v3) { //Overloading
return v1+v2+v3;
}
}
Cal
클래스에 형태만 다르고 이름은 같은 메소드를 정의해보았다.
실행시켜보면 자바는 인자 수가 같은 sum(int v1, int v2, int v3)
를 찾아서 실행시켜준다는 것을 알 수 있다. 위와 같은 sum 함수를 Overloading이라고 한다.
class Cal{
public int sum(int v1, int v2) {
return v1+v2;
}
}
class Cal3 extends Cal{
public int sum(int v1, int v2) { //overriding
System.out.println("Cal3!!");
return v1+v2;
}
public int minus(int v1, int v2) {
return v1-v2;
}
public int sum(int v1, int v2, int v3) { //overriding* overloading
return v1+v2+v3;
}
}
이렇게 Cal3
로 위에서 새로 만든 sum(,,)
을 이동시키면, 이 함수는 부모가 없는 기능을 추가하면서, 이름이 같은 것과 함께 공존하는 오버라이딩이자 오버로딩인 것이다.
참고로, 오버로딩은 상속과는 관계가 없다!
this
는 자기 자신, super
은 자신의 부모를 나타낸다.
Cal3
의 sum
함수가 부모의 sum
함수를 호출하고, 그 결과를 이용할 수 있다면 얼마나 좋을까!
class Cal3 extends Cal{
public int sum(int v1, int v2) { //overriding
System.out.println("Cal3!!");
return super.sum(v1,v2);
}
public int minus(int v1, int v2) {
return v1-v2;
}
}
그럴땐 super
을 사용하면 된다. 그러면 부모클래스의 sum
을 가리킨다!
디버거를 이용해 Step into
로 확인해보면 return super.sum을 할 때 부모의 sum 메소드를 호출하는 것을 확인할 수 있다!
this
는 자기 자신의 인스턴스를 나타낸다.
class Cal{
public int sum(int v1, int v2) {
return v1+v2;
}
public int sum(int v1, int v2, int v3) {//overloading
return this.sum(v1,v2)+v3;//sum(v1,v2)+v3 is ok.
}
}
자기자신(this)의 sum의 실행시킨 결과이니 위에 있는 sum함수를 호출하게 된다.
일단 예제가 복잡해져서 필요없는 것은 지워버리자.
class Cal{
}
class Cal3 extends Cal{
}
public class InheritanceApp {
public static void main(String[] args) {
Cal c=new Cal();
Cal3 c3=new Cal3();
}
}
그리고 Cal
이라는 클래스에 생성자가 있다면 어떨지를 확인해보자.
처음 생성할 때 Cal(2,3); 으로 생성되도록 해보자.
class Cal{
int v1,v2;
Cal(int v1, int v2){
this.v1=v1;
this.v2=v2;
}
}
class Cal3 extends Cal{
}
public class InheritanceApp {
public static void main(String[] args) {
Cal c=new Cal(2,1);
Cal3 c3=new Cal3();
}
}
이렇게 했더니.. Cal3
에 빨간 밑줄이 그인다.
Cal을 제대로 계승했다면, Cal이 인스턴스로 만들어질 때 반드시 해야 할 일이 생성자에 들어있다. Cal3도 실행을 시켜야지 제대로 계승하는 것이다.
상속받은 부모가 생성자가 있다면 반드시 자식은 부모 생성자를 실행시키도록 강제하고 있다.
오류 부분에 마우스를 가져다대면 생성자를 만드는 버튼이 있는데, IDE가 알아서 생성자를 생성해준다.
class Cal3 extends Cal{
Cal3(int v1, int v2) {
super(v1, v2);
// TODO Auto-generated constructor stub
}
}
그러면 이렇게 생성자가 알아서 만들어진다.
여기서 super
는 부모 클래스를 뜻하고 super(v1,v2)
는 부모클래스의 생성자를 뜻한다.
class Cal{
int v1,v2;
Cal(int v1, int v2){
System.out.println("Cal init!!!");
this.v1=v1;
this.v2=v2;
}
}
class Cal3 extends Cal{
Cal3(int v1, int v2) {
super(v1, v2);
System.out.println("Cal3 init!!");
}
}
public class InheritanceApp {
public static void main(String[] args) {
Cal c=new Cal(2,1);
Cal3 c3=new Cal3(2,1);
}
}
이 소스코드를 실행해보자!
처음 Cal
인스턴스를 생성했을 때는 Cal
클래스의 생성자만 호출되지만, Cal3
의 인스턴스를 생성했을 때는 부모클래스의 생성자와 자기 자신의 생성자를 호출한다!
아까 없앴던 sum함수와 minus를 다시 도입하고, 계산 결과를 확인해보자!
class Cal{
int v1,v2;
Cal(int v1, int v2){
System.out.println("Cal init!!!");
this.v1=v1;
this.v2=v2;
}
public int sum() {
return this.v1+this.v2;
}
}
class Cal3 extends Cal{
Cal3(int v1, int v2) {
super(v1, v2);
System.out.println("Cal3 init!!");
}
public int minus() {
return this.v1-this.v2;
}
}
public class InheritanceApp {
public static void main(String[] args) {
Cal c=new Cal(2,1);
Cal3 c3=new Cal3(2,1);
System.out.println(c3.sum()); //3
System.out.println(c3.minus()); //1
}
}
더이상 sum함수와 minus함수에 인자가 필요가 없게 된다.
다형성(Polymorphism)
: 자식클래스를 부모클래스로 동작하도록 규제하는 기술
ParentClass obj=new ChildClass()
Final
다른 사람이 더이상 상속하지 못하거나 오버라이딩 하지 못하게, 변수를 마음대로 수정하지 못하게 만든다.
Abstract
클래스를 상속해서 사용하려는 사용자에게 어떤 특정한 메소드는 꼭 구현하라고 강조하고 싶을 때 사용한다.
보아야 할 강의가 엄청 많았다..
왜 저번에 적다고 생각했지?!
아무튼 다음은 자바의 인터페이스
에 대해서 알아보자!