다형성 활용 1

public class Cat {
public void sound(){
System.out.println("야옹");
}
}
public class Cow {
public void sound(){
System.out.println("음모");
}
}
public class Dog {
public void sound(){
System.out.println("멍멍");
}
}
public class AnimalSoundMain {
public static void main(String[] args) {
Dog dog = new Dog();
Cat cat = new Cat();
Cow cow = new Cow();
System.out.println("동물 소리 시작");
dog.sound();
System.out.println("동물 소리 종료");
System.out.println("동물 소리 시작");
cat.sound();
System.out.println("동물 소리 종료");
soundCow(cow);
}
private static void soundCow(Cow cow){
System.out.println("동물 소리 테스트 시작");
cow.sound();
System.out.println("동물 소리 테스트 종료");
}
}
중복 되는 부분
System.out.println("동물 소리 시작");
dog.sound();
System.out.println("동물 소리 종료");
System.out.println("동물 소리 시작");
cat.sound();
System.out.println("동물 소리 종료");
soundCow(cow);
배열을 통해 중복 제거 시도
Caw[] cawArr = {cat, dog, caw}; //컴파일 오류 발생!
System.out.println("동물 소리 테스트 시작");
for (Caw caw : cawArr) {
cawArr.sound();
}
System.out.println("동물 소리 테스트 종료");
Dog, Cat, Caw 타입이 서로 다르기 때문에 불가능
반대로 이야기 하면 Dog, Cat, Caw type이 같다면 메서드와 배열을 활용해 코드의 중복 제거 가능
다형성 활용 2

public class Animal {
public void sound(){
System.out.println("동물 울음 소리");
}
}
public class Cat extends Animal{
@Override
public void sound(){
System.out.println("야옹");
}
}
public class Cow extends Animal{
@Override
public void sound(){
System.out.println("음모");
}
}
public class Dog extends Animal{
@Override
public void sound(){
System.out.println("멍멍");
}
}
public class AnimalSoundMain2 {
public static void main(String[] args) {
Dog dog = new Dog();
Cat cat = new Cat();
Cow cow = new Cow();
soundAnimal(dog);
soundAnimal(cat);
soundAnimal(cow);
}
private static void soundAnimal(Animal animal){
System.out.println("동물 울음 소리 시작");
animal.sound();
System.out.println("동물 울음 소리 종료");
}
}
코드 분석

soundAnimal(dog) 호출
soundAnimal(Animal animal)에 Dog 인스턴스 전달
Animal animal = dog로 이해
메서드 안에서 animal.sound 호출
Dog 클래스 내부에도 sound 메서드가 있기 때문에 오버라이딩 우선 순위. -> 메서드 우선권
다형성 활용 3
public class AnimalSoundMain3 {
public static void main(String[] args) {
Dog dog = new Dog();
Cat cat = new Cat();
Cow cow = new Cow();
//추가 되더 라도 변하지 않는 것
Animal[] all = new Animal[]{dog, cat, cow};
for (Animal animal : all) {
System.out.println("동물 소리 시작");
animal.sound();
System.out.println("동물 소리 끝");
}
}
}
아직 문제 점이 남아있다면 Animal 클래스를 생성할 수 있는 문제, Animal 클래스를 상속 받는 곳에서 sound() 메서드 오버라이딩 하지 않을 가능성
Animal 클래스를 생성할 수 있는 문제
Animal 현 기능은 sound 메서드를 제공하는 것 외에는 별다른 기능 X, 그런데 new Animal 인스턴스 할당해준다면? -> 문제 발생
Animal 클래스 상속 받는 곳에서 sound 메서드를 오버라이딩 하지 않을 가능성 O
메서드 오버라이딩 하는 것을 잊어 먹을 수 도 있다.
추상 클래스
부모 클래스는 제공하지만, 실제 생성이 되면 안되는 클래스를 추상 클래스.
추상 클래스는 이름 그대로 추상적 개념을 제공, 실체인 인스턴스 존재 X
abstract class AbstractAnimal {...}
추상 클래스는 클래스를 선언할때 추상이라는 의미 -> abstract 사용
추상 메서드
부모 클래스를 상속 받는 자식 클래스가 반드시 오버라이딩 해야 하는 메서드를 부모 클래스에 정의 X . -> 추상 메서드
추상적 개념을 제공하는 메서드, 따라서 실체 존재 X, 메서드 바디 X
public abstract void sound();
추상 메서드는 선언한 메서드 앞에 추상이라는 abstract
추상 메서드가 하나라도 있는 클래스는 추상 클래스로 선언
-> 사용 X => CompileError
-> 추상 메서드는 메서드 바디 X, 따라서 작동하지 않는 메서드를 가진 불완전한 클래스.
추상 메서드는 상속 받는 자식 클래스가 반드시 오버라이딩
-> 사용 X = CompileError
-> 자식 클래스가 반드시 오버라이딩 하기 때문에 바디 부분 X
-> 오버라이딩 하지 않을 꺼면 자식 또한 abstract

public abstract class AbstractAnimal {
//추상이 붙은 메서드 => 자식에서 반드시 오버라이딩
public abstract void sound();
public void move(){
System.out.println("동물이 움직인다");
}
}
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 Dog extends AbstractAnimal{
@Override
public void sound() {
System.out.println("명멍");
}
}
public class AbstractMain {
public static void main(String[] args) {
Dog dog = new Dog();
Cat cat = new Cat();
Caw caw = new Caw();
caw.move();
caw.sound();
soundAnimal(cat);
soundAnimal(dog);
}
private static void soundAnimal(AbstractAnimal animal){
System.out.println("시작");
animal.sound();
System.out.println("끝");
}
}
추상 메서드는 반드시 오버라이딩 -> 자식에서 오버라이딩 메서드를 만들지 X -> CompileError

추상 클래스 2
move 또한 추상 클래스로 변경 ??
AbstractAnimal에 메서드가 모두 추상메서드로 변경 = 순수 추상 클래스

public abstract class AbstractAnimal {
//추상이 붙은 메서드 => 자식에서 반드시 오버라이딩
public abstract void sound();
public abstract void move();
}
public class Cat extends AbstractAnimal {
@Override
public void sound() {
System.out.println("야옹");
}
@Override
public void move() {
System.out.println("이동");
}
}
public class Caw extends AbstractAnimal {
@Override
public void sound() {
System.out.println("음매");
}
@Override
public void move() {
System.out.println("이동");
}
}
public class Dog extends AbstractAnimal {
@Override
public void sound() {
System.out.println("명멍");
}
@Override
public void move() {
System.out.println("이동");
}
}
public class AbstractMain {
public static void main(String[] args) {
Dog dog = new Dog();
Cat cat = new Cat();
Caw caw = new Caw();
caw.move();
caw.sound();
caw.move();
soundAnimal(cat);
soundAnimal(dog);
}
private static void soundAnimal(AbstractAnimal animal){
System.out.println("시작");
animal.sound();
System.out.println("끝");
}
private static void moveAnimal(AbstractAnimal animal){
System.out.println("이동 시작");
animal.move();
System.out.println("이동 끝");
}
}

상속하는 클래스는 모든 메서드를 구현
상속시 자식은 모든 메서드를 오버라이딩 -> 상속 받은 클래스 입장에서 보면 부모 모든 메서드를 구현
일반적으로 이야기하는 인터페이스와 같이 느껴짐
인터페이스
public interface InterfaceAnimalMain {
void move();
void sound();
}
public class Cat implements InterfaceAnimalMain{
@Override
public void move() {
System.out.println("이동");
}
@Override
public void sound() {
System.out.println("야옹");
}
}
public class Caw implements InterfaceAnimalMain {
@Override
public void sound() {
System.out.println("음매");
}
@Override
public void move() {
System.out.println("이동");
}
}
public class Dog implements InterfaceAnimalMain {
@Override
public void sound() {
System.out.println("명멍");
}
@Override
public void move() {
System.out.println("이동");
}
}
public class AnimalInterfaceMain {
public static void main(String[] args) {
Cat cat = new Cat();
Dog dog = new Dog();
Caw caw = new Caw();
soundAnimal(cat);
soundAnimal(dog);
soundAnimal(caw);
}
private static void soundAnimal(InterfaceAnimalMain main){
System.out.println("소리 시작");
main.sound();
System.out.println("소리 끝");
}
}
순수 추상클래스는 인스턴스 생성X, 상속시 모든 메서드 오버라이딩, 주로 다형성을 위해 사용
인터페이스는 앞서 설명한 순수 추상 클래스와 동일 + 일부 추가기능
인터페이스의 메서드는 모두 public, abstract
메서드에 public abstract를 생략
인터페이스는 다중 구현을 지원
인터페이스와 멤버 변수
public interface InterfaceAnimal {
public static final double MY_PI = 3.14;
}
인터페이스에서 멤버 벼눗는 public static final 모두 포함


클래스, 추상 클래스, 인터페이스 모두 동일
상속 vs 구현
부모 클래스의 기능을 자식 클래스가 상속, -> 클래스는 상속 받는다 표현, 부모 인터페이스의 기능을 자식이 상속 받을 때는 인터페이스를 구현.
상속은 이름 그대로 부모의 기능을 물려 받는 것 목적. 하지만 인터페이스는 모든 메서드가 추상 메서드, 따라서 물려받는 기능 X, 모든 메서드를 자식이 오버라이딩 해서 기능을 구현.
인터페이스는 메서드 이름만 있는 설계도, 실제 어떻게 작동하는지 하위 클래스에서 모두 구현.
인터페이스를 사용하는 이유
모든 메서드가 추상 메서드인 경우 순수 추상 클래스를 형성, 인터페이를 만들어도 된다.
제약 :
인터페이스를 이용하는 이유는 구현하는 곳에서 메서드를 반드시 구현해줘야하는 제약 O
인터페이스의 규약은 반드시 구현해야 하는 것. 그런데 순수 추상 클래스의 경우 미래에 누군가 그곳에 실행 가능한 메서드를 끼워 넣을 수 도 있다. 이렇게 되면 추가 기능을 자식 클래스에서 구현하지 않을 수도 있고, 순수 추상 클래스가 아니게 된다.
다중구현:
자바 클래스 상속은 부모를 하나만 지정, 반면에 인터페이스는 부모를 여러명 두는 다중 상속 가능
인터페이스 다중 구현

인터페이스 자신은 구현을 가지지 않는다. 대신 인터페이스를 구현하는 곳에서 해당 기능을 모두 구현. InterfaceA, InterfaceB는 같은 이름 methodCommon을 제공, 이 기능은 child가 구현. 그리고 오버라이딩에 의해 어차피 child에 있는 methodCommon이 호출.
결과적으로 두 부모중 어떤 한 부모의 methodCommon을 선택하는 것이 아니라 그냥 인터페이스들을 구현한 child에 있는 methodCommon이 사용
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");
}
}
public class DiamondMain {
public static void main(String[] args) {
InterfaceA a = new Child();
a.methodA();
a.methodCommon();
InterfaceB b = new Child();
b.methodB();
b.methodCommon();
}
}


클래스와 인터페이스 활용

public interface Fly {
void fly();
}
public abstract class AbstractAnimal {
public abstract void sound();
public void move(){
System.out.println("동물이 이동 합니다");
}
}
public class Bird implements Fly{
@Override
public void fly() {
System.out.println("펄럭");
}
}
public class Cat extends AbstractAnimal{
@Override
public void sound() {
System.out.println("명멍");
}
}
public class Dog extends AbstractAnimal{
@Override
public void sound() {
System.out.println("야옹");
}
}
public class AnimalMain {
public static void main(String[] args) {
Dog dog = new Dog();
Bird bird = new Bird();
Cat cat = new Cat();
soundAnimal(dog);
soundAnimal(cat);
flyAnimal(bird);
}
private static void soundAnimal(AbstractAnimal animal){
System.out.println("시작");
animal.sound();
System.out.println("끝");
}
private static void moveAnimal(AbstractAnimal animal){
System.out.println("시작");
animal.move();
System.out.println("끝");
}
private static void flyAnimal(Fly fly){
System.out.println("시작");
fly.fly();
System.out.println("끝");
}
}


코드 설명은 생략하겠다...