티스토리 뷰

JAVA

다형성2

Howu 2024. 1. 15. 17:02

1. 메서드 오버라이딩

package poly.overriding;
public class Parent {
    public String value = "parent";
    public void method() {
        System.out.println("Parent.method");
} }

package poly.overriding;
public class Child extends Parent {
    public String value = "child";
    @Override
    public void method() {
        System.out.println("Child.method");
    }
}

=> Child에서 Parent의 메서드를 오버라이딩 하였다.

 

package poly.overriding;
public class OverridingMain {
    public static void main(String[] args) {
    
	//자식 변수가 자식 인스턴스 참조
	Child child = new Child(); 
    	System.out.println("Child -> Child"); 
    	System.out.println("value = " + child.value); child.method();
    
	//부모 변수가 부모 인스턴스 참조
	Parent parent = new Parent(); 
    	System.out.println("Parent -> Parent"); 
    	System.out.println("value = " + parent.value); parent.method();

	//부모 변수가 자식 인스턴스 참조(다형적 참조)
	Parent poly = new Child();
	System.out.println("Parent -> Child"); 
    	System.out.println("value = " + poly.value); //변수는 오버라이딩X poly.method(); //메서드 오버라이딩!
	} 
}

=> Child -> Child

=> value = child

=> Child.method

 

=> Parent -> Parent

=> value = parent

=> Parent.method

 

=> Parent -> Child

=> value = parent (변수는 오버라이딩 되지 않으므로 parent로 나오고)

=> Child.method (메서드 오버라이딩으로 child method를 우선으로 호출)

=> poly 변수는 Parent 타입으로 method를 호출 했을 때 오버라이딩 된 메서드가 항상 우선권을 가지므로 Parent.method()가 아닌 Child.method()가 실행된다. 기존 기능을 하위 타입에서 새로운 기능으로 재정의하여 사용할 수 있다.

 

2. 다형성 활용법

package poly.ex2;
 public class Animal {
     public void sound() {
	System.out.println("동물 울음 소리"); }
}

=> 최상위 부모로 Animal 클래스를 만들고 sound 메서드를 정의해놓고 하위 클래스에서 재정의하여 사용할 수 있다.

 

package poly.ex2;

public class Dog extends Animal {
     @Override
	 public void sound() { 
     System.out.println("멍멍");
     }
}

public class Cat extends Animal {
    @Override
	public void sound() { 
	System.out.println("냐옹");
	} 
}

public class Caw extends Animal{
    @Override
	public void sound() { 
	System.out.println("음매");
	} 
}

 

package poly.ex2;
public class AnimalPolyMain1 {
    public static void main(String[] args) {
        Dog dog = new Dog();
        Cat cat = new Cat();
        Caw caw = new Caw();
        soundAnimal(dog);
        soundAnimal(cat);
        soundAnimal(caw);
}
//동물이 추가 되어도 변하지 않는 코드
private static void soundAnimal(Animal animal) {
	System.out.println("동물 소리 테스트 시작"); 
    	animal.sound();
	System.out.println("동물 소리 테스트 종료");
	}
}

=> soundAnimal 메서드의 매개변수 타입을 공통 부모인 Animal 클래스로 받아서 sound() 메서드를 호출하게 되면 동물이 추가 되어도 변하지 않는 코드가 된다. 구체적인 클래스를 참조하는 것이 아니라 Animal이라는 추상적인 부모를 참조할 수 있기 때문이다. 만약에 사자가 추가 된다고 하더라도 객체 생성하고 그 객체를 매개변수로 갖는 메서드를 호출하기만 하면 된다.

 

=> 실행결과

  • 동물  소리 테스트 시작
  • 멍멍
  • 동물  소리 테스트 종료
  • 동물  소리 테스트 시작
  • 냐옹
  • 동물  소리 테스트 종료
  • 동물  소리 테스트 시작
  • 음매
  • 동물  소리 테스트 종료

=> animal.sound()로 호출하게 되면 Dog, Cat, Caw 어떤 동물이든 부모의 메서드를 재정의 한 자식의 메서드를 호출하여 사용할 수 있으므로 동물이 바뀌고 추가되어도 매개변수와 메서드 추가 없이 단일한 코드로 활용할 수 있다.

 

package poly.ex2;
 public class AnimalPolyMain2 {
     public static void main(String[] args) {
         Dog dog = new Dog();
         Cat cat = new Cat();
         Caw caw = new Caw();
         Animal[] animalArr = {dog, cat, caw};
         //Animal[] animalArr = new Animail[]{dog, cat, caw};
         
//변하지 않는 부분
for (Animal animal : animalArr) {
	System.out.println("동물 소리 테스트 시작"); 
    	animal.sound();
	System.out.println("동물 소리 테스트 종료");
		} 
	}
}

=> Animal 타입의 배열을 만들고 여러 타입의 객체를 참조한다.

=> 배열을 사용한 중복 제거로 실행 결과는 위와 동일.

 

3.  추상 클래스와 추상 메서드

  • 추상 클래스: 부모 클래스는 제공하지만 실제로 생성하면 안되는 클래스로 추상적인 개념을 제공하는 클래스. 상속을 목적으로 사용되며 인스턴스를 만들 수 없다.
  • 추상 메서드: 부모 클래스를 상속 받는 자식 클래스가 반드시 오버라이딩 해야 하는 메서드를 부모 클래스에 정의할 수 있다. 추상 메서드가 하나라도 있는 클래스는 추상 클래스가 되어야 한다. 추상 메서드는 상속을 받게 된 자식 클래스가 반드시 오버라이딩하여 사용해야 한다.
package poly.ex3;
public abstract class AbstractAnimal {
    
    //인스턴스를 생성할 수 없는 추상 메서드로 반드시 자식이 오버라이딩하여 사용해야 한다.
    public abstract void sound();
    
    //인스턴스를 생성할 수 있는 그냥 메서드로 자식이 오버라이딩 하지 않아도 된다.
	public void move() { 
	System.out.println("동물이 움직입니다.");
	}
}

 

=> 추상 메서드는 인스턴스를 생성할 수 없고 자식클래스가 반드시 오버라이딩하여 사용해야만 한다.

 

package poly.ex3;
 public class Cat extends AbstractAnimal {
     @Override
	public void sound() { 
	System.out.println("냐옹");
	} 
}

public class Caw extends AbstractAnimal {
     @Override
	public void sound() { 
	System.out.println("음매");
	} 
}

 public class AbstractMain {
     public static void main(String[] args) {
	//추상클래스 생성 불가
	//AbstractAnimal animal = new AbstractAnimal();
         Dog dog = new Dog();
         Cat cat = new Cat();
         Caw caw = new Caw();
         cat.sound();
         cat.move();
         soundAnimal(cat);
         soundAnimal(dog);
         soundAnimal(caw);
}

//동물이 추가 되어도 변하지 않는 코드
private static void soundAnimal(AbstractAnimal animal) {
	System.out.println("동물 소리 테스트 시작"); 
    	animal.sound();
	System.out.println("동물 소리 테스트 종료");
} }

=> 실행결과

냐옹 //cat.sound();
동물이 움직입니다
. //cat.move();

  • 동물  소리 테스트 시작
  • 냐옹
  • 동물  소리 테스트 종료
  • 동물  소리 테스트 시작
  • 멍멍
  • 동물  소리 테스트 종료
  • 동물  소리 테스트 시작
  • 음매
  • 동물  소리 테스트 종료

4. 인터페이스

 public interface InterfaceAnimal {
     void sound();
     void move();
 }

=> public abastract를 생략할 수 있는 추상 메서드의 집합, 인터페이스

=> 특징1: 인스턴스를 생성할 수 없고, 상속 시 모든 메서드를 오버라이딩 해야 한다.

=> 특징2: 인터페이스에서의 멤버변수는 public, static, final이 모두 포함되었다고 간주된다.

 

public class Dog implements InterfaceAnimal {

@Override
    public void sound() {
    	System.out.println("멍멍");
    }
    
@Override
    public void move() {
    	System.out.println("개 이동");
    }
    
}

=> extends 대신 implements 키워드를 사용한다. 인터페이스의 추상메서드를 오버라이딩하여 사용한다.

 

public class InterfaceMain {
	public static void main(String[] args) {

        Dog dog = new Dog();
        soundAnimal(dog);
}

//동물이 추가 되어도 변하지 않는 코드
private static void soundAnimal(InterfaceAnimal animal) {
	System.out.println("동물 소리 테스트 시작"); 
	animal.sound();
	System.out.println("동물 소리 테스트 종료");
	} 
}

=> 실행결과

동물 소리 테스트 시작

멍멍

동물 소리 테스트 종료

 

=> 상속과 구현의 차이

상속은 부모의 기능을 물려 받는 것이 목적이고, 인터페이스의 구현은 물려받을 수 있는 기능은 없고 인터페이스에 정의한 모든 메서드를 자식이 오버라이딩해서 구현해야만 한다.

 

=> 다중 구현

인터페이스를 구현하는 곳에서 해당 기능을 모두 구현해야 한다.

public interface InterfaceA {
	 void methodA();
     void methodCommon();
}

public interface InterfaceB {
	void methodB();
    void methodCommon();
}

public class Child implements InterfaceA, InterfaceB {

	@Override
    public void methodA() {
    	System.out.println("Child.methodA");
    }
    @Override
    public void methodB() {
    	System.out.println("Child.methodB");
    }
    @Override
    public void methodCommon() {
    	System.out.println("Child.methodCommon");
    }
}

=> implements InterfaceA, InterfaceB와 같이 다중 구현을 할 수 있다.

=> methodCommon()의 경우 양쪽 인터페이스에 다 있지만 같은 메서드이기 때문에 구현은 하나만 하면 된다.

 

public class InterfaceMain {
	public static void main(String[] args) {
    	InterfaceA a = new Child();
        a.methodA();
        a.methodCommon();
        
        InterfaceB b = new Child();
        b.methodB();
        b.methodCommon();
    }
}

실행결과

Child.methodA

Child.methodCommon

Child.methodB

Child.methodCommon

 

'JAVA' 카테고리의 다른 글

[멀티스레드와 동시성] - 1. 프로세스와 스레드  (0) 2024.07.25
자바 채팅 프로그램  (0) 2024.05.29
다형성1  (0) 2024.01.07
1. JDK, JRE, JVM  (0) 2023.11.29
JAVA | null과 "" | 개념  (2) 2023.08.03
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/02   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28
글 보관함