개발

[Java] 예제 코드로 알아보는 다형성 - 1

오렌지색 귤 2022. 1. 24. 11:57
반응형

정의

 

다형성(polymorphism)이란 하나의 객체가 여러 가지 타입을 가질 수 있는 것을 의미합니다.

자바에서는 이러한 다형성을 부모 클래스 타입의 참조 변수로 자식 클래스 타입의 인스턴스를 참조할 수 있도록 하여 구현하고 있습니다.

다형성은 상속, 추상화와 더불어 객체 지향 프로그래밍을 구성하는 중요한 특징 중 하나입니다.

참고 Tcp School

 

 

 

 

저도 다형성의 정의에 대해서는 이해하고 있다고만 생각했습니다. 왜냐하면 아래와 같은 코드들을 활용한다면 다형성을 충분히 이용하는 것이라 생각했기 때문입니다.

 

class Animal {...}
class Human extends Animal {...}
class Korean extends Human {...}

public class Main {
    public static void main(String[] args) {
        List<Animal> list = new ArrayList<>();
        list.add(new Animal());
        list.add(new Human());
        list.add(new Korean());
    }
}

 

위의 코드에서는 ArrayList 구체 클래스 대신 List 라는 인터페이스를 사용하는 다형성과 Animal 타입의 제네릭스를 가진 리스트를 선언하면서 Animal의 하위 클래스들 모두 list에 저장할 수 있는 다형성이 사용되고 있습니다.

 

그렇지만 이것이 다형성의 전부는 아닙니다. 지금부터 하나씩 알아보도록 하겠습니다.

 

 

상속

 

변수의 scope

 

Quiz 1. 아래 코드의 결과를 예측하시오.

 

1) 현재 코드의 결과
2) 9번째 줄을 주석 처리 했을 때의 결과

3) 6번째 줄과 9번째 줄을 모두 주석 처리 했을 때의 결과

 

class Parent {
    int x = 1;
}

class Child extends Parent {
    int x = 2;    // 6번째 줄

    void call() {
        int x = 3;    // 9번째 줄
        System.out.printf("%d %d %d\n", x, this.x, super.x);
    }
}

class Main {
    public static void main(String[] args) {
        Child child = new Child();
        child.call();
    }
}

 

 

 

 

정답

 

1) 3 2 1
2) 2 2 1
3) 1 1 1

 

사용된 위치에서 가장 가깝게 선언된 변수를 가져오게 되고, method 내부에서 해당 클래스의 멤버 변수를 거쳐 조상 클래스의 멤버 변수까지 확인하게 됩니다.

 

 

 

 

this, super 키워드

 

this.a - 현재 객체에 존재하는 필드 a의 값
this(String name) - 현재 클래스에서 String name을 매개변수로 가지는 생성자 호출
super.a - 상속받은 상위 클래스에 존재하는 필드 a의 값
super(int number) - 상속받은 상위 클래스에서 int number를 매개변수로 가지는 생성자 호출

 

특히 this() 또는 super()는 모든 생성자의 첫 줄에 쓰이며(중복 불가), 따로 작성하지 않더라도 컴파일러가 super()를 자동 생성합니다.


결국 모든 클래스들은 Object를 상속받고 있는 구조인 Java에서 모든 클래스의 객체를 만들 때 해당 클래스의 상위 클래스들의 생성자를 모두 거쳐 Object 객체가 생성됩니다.


이런 점에서 Java가 객체 지향 언어의 대표라는 것을 다시금 느끼게 됩니다.

 

아래 그림에서 생성자 호출과 객체 생성의 단계를 한눈에 살펴보실 수 있습니다.

 

 

 

 

 

 

메서드 재정의 (오버라이드)

 

Quiz 2. 아래 코드의 결과를 예측하시오.

 

public class Animal {
    private int legs;

    public Animal(int legs) {
        this.legs = legs;
    }
}

public class Human extends Animal {
    private String name;

    public Human(int legs, String name) {
        super(legs);
        this.name = name;
    }
}

public class Korean extends Human {
    private String favoriteFood;

    public Korean(int legs, String name, String favoriteFood) {
        super(legs, name);
        this.favoriteFood = favoriteFood;
    }
}

public class Test {
    public static void main(String[] args) {
        Korean k1 = new Korean(2, "김상식", "김치");
        Korean k2 = new Korean(2, "이지혜", "불고기");
        Korean k3 = new Korean(2, "김상식", "김치");

        System.out.println(k1.toString());
        System.out.println(k2.toString());
        System.out.println(k3.toString());

        System.out.println(k1.equals(k2));
        System.out.println(k2.equals(k3));
        System.out.println(k3.equals(k1));
    }
}

 

 

 

 

정답

 

doodle.polymorphism.Korean@42a57993
doodle.polymorphism.Korean@75b84c92
doodle.polymorphism.Korean@6bc7c054
false
false
false

 

어라? 나는 한국인을 출력하면 어떤 정보가 나올 줄 알았는데 클래스 구조에 해시코드가 출력된다.


또, k1과 k3는 모든 필드의 값이 같으니 같은 사람으로 인식되어야 할 것 같은데 false가 나온다.

 

그런데 잠깐!


곰곰히 생각해보니 toString() 메서드와 equals() 메서드는 내가 전혀 만들어 준적이 없다.


그런데 어떻게 내가 만든 Korean 객체에서 해당 메서드를 사용 가능한거지?

 

사실 두 메서드는 모두 Object 클래스에 정의된 메서드이다.


위에서도 말했듯, 모든 클래스는 Object 클래스를 상속하므로, 해당 클래스나 상위 클래스에 Object가 가진 메서드가 오버라이딩 되지 않았다면 Object의 메서드를 그대로 사용하게 된다.

 

 

 

 

Quiz 3. Animal 클래스를 약간 수정했다. 다음 코드의 결과를 예측하시오.

 

public class Animal {

    private int legs;

    public Animal(int legs) {
        this.legs = legs;
    }

    @Override
    public String toString() {
        return "Animal{" + "legs=" + legs + '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o)
            return true;
        if (o == null || getClass() != o.getClass())
            return false;
        Animal animal = (Animal)o;
        return legs == animal.legs;
    }

    @Override
    public int hashCode() {
        return Objects.hash(legs);
    }
}

... // 기타 생략

public class Test {
    public static void main(String[] args) {
        Korean k1 = new Korean(2, "김상식", "김치");
        Korean k2 = new Korean(2, "이지혜", "불고기");
        Korean k3 = new Korean(2, "김상식", "김치");

        System.out.println(k1.toString());
        System.out.println(k2.toString());
        System.out.println(k3.toString());

        System.out.println(k1.equals(k2));
        System.out.println(k2.equals(k3));
        System.out.println(k3.equals(k1));
    }
}

 

 

 

 

 

정답

 

Animal{legs=2}
Animal{legs=2}
Animal{legs=2}
true
true
true

 

toString() 메서드와 equals() 메서드를 상위 클래스인 Animal 클래스에서 재정의했다.


더 이상 Object 클래스의 메서드는 사용하지 않는 것으로 보이지만... 뭔가 잘못된 것 같다.


우리는 한국인을 출력했는데 다리가 2개인 동물이라고만 출력되고, 이름과 좋아하는 음식이 다른 사람도 같다고 해버린다.

 

 

 

 

 

Quiz 4. 많은 수정을 거친 다음 코드의 결과를 예측하자.

 

public class Animal {

    private int legs;

    public Animal(int legs) {
        this.legs = legs;
    }

    @Override
    public String toString() {
        return "Animal{" +
            "legs=" + legs +
            '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o)
            return true;
        if (o == null || getClass() != o.getClass())
            return false;
        Animal animal = (Animal)o;
        return legs == animal.legs;
    }

    @Override
    public int hashCode() {
        return Objects.hash(legs);
    }
}

public class Human extends Animal {
    private String name;

    public Human(int legs, String name) {
        super(legs);
        this.name = name;
    }

    @Override
    public String toString() {
        return super.toString() + "Human{" + "name='" + name + '\'' + '}';
    }
}

public class Korean extends Human {
    private String favoriteFood;

    public Korean(int legs, String name, String favoriteFood) {
        super(legs, name);
        this.favoriteFood = favoriteFood;
    }

    @Override
    public String toString() {
        return super.toString() + "Korean{" + "favoriteFood='" + favoriteFood + '\'' + '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o)
            return true;
        if (o == null || getClass() != o.getClass())
            return false;
        if (!super.equals(o))
            return false;
        Korean korean = (Korean)o;
        return Objects.equals(favoriteFood, korean.favoriteFood);
    }

    @Override
    public int hashCode() {
        return Objects.hash(super.hashCode(), favoriteFood);
    }
}

public class Test {
    public static void main(String[] args) {
        Korean k1 = new Korean(2, "김상식", "김치");
        Korean k2 = new Korean(2, "이지혜", "불고기");
        Korean k3 = new Korean(2, "김상식", "김치");

        System.out.println(k1.toString());
        System.out.println(k2.toString());
        System.out.println(k3.toString());

        System.out.println(k1.equals(k2));
        System.out.println(k2.equals(k3));
        System.out.println(k3.equals(k1));
    }
}

 

 

 

 

 

정답

 

Animal{legs=2}Human{name='김상식'}Korean{favoriteFood='김치'}
Animal{legs=2}Human{name='이지혜'}Korean{favoriteFood='불고기'}
Animal{legs=2}Human{name='김상식'}Korean{favoriteFood='김치'}
false
false
true

 

이제 비로소 우리가 원하던 결과가 나온 듯하다.


toString() 메서드는 "다리가 2개이며 김상식이라는 이름을 가진 인간은 김치를 좋아하는 한국인이다." 라는 원하던 구체적인 객체 정보를 출력해준다.


또한, 이름과 좋아하는 음식이 같은 k1과 k3는 같다고 인식된다.

 

 

 

이어지는 포스팅

2022.01.24 - [분류 전체보기] - [Java] 예제 코드로 알아보는 다형성 - 2

 

[Java] 예제 코드로 알아보는 다형성 - 2

2022.01.24 - [Java] - [Java] 예제 코드로 알아보는 다형성 - 1 [Java] 예제 코드로 알아보는 다형성 - 1 정의 다형성(polymorphism)이란 하나의 객체가 여러 가지 타입을 가질 수 있는 것을 의미합니다. 자바에

hwan33.tistory.com

 

반응형