[코드스테이츠 백엔드 44기 SEB BE] 13일차

오태호·2023년 3월 2일
0

코드스테이츠

목록 보기
12/22
post-thumbnail

다형성(Polymorphism)

  • 하나의 객체가 여러가지 형태를 가질 수 있는 성질
  • 한 타입의 참조 변수를 통해 여러 타입의 객체를 참조할 수 있도록 만든 것
  • 상위 클래스 타입의 참조변수를 통해 하위 클래스의 객체를 참조할 수 있도록 허용한 것
  • 다형성을 사용하는 이유
    • 다형성을 잘 활용하면 많은 중복되는 코드를 줄이고 보다 편리하게 코드를 작성할 수 있다!
class Animal {
    public void print() {
        System.out.println("Animal 클래스");
    }
}

class Dog extends Animal {
    public void print() {
        System.out.println("Dog 클래스");
    }
}

class Cat extends Animal {
    public void print() {
        System.out.println("Cat 클래스");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal animal = new Animal();
        Animal dog = new Dog();
        Cat cat = new Cat();
    }
}
  • Animal 타입의 참조변수 dog를 보면 Dog 클래스의 인스턴스를 생성하고 dog에 할당
    • 상위 클래스를 참조변수의 타입으로 지정하여 참조변수가 사용할 수 있는 멤버의 개수는 상위 클래스의 멤버의 수가 된다
  • 상위 클래스의 타입으로 하위 클래스의 타입의 객체를 참조할 수 있지만 그 반대는 성립하지 않는다!
    • 실제 참조하고 있는 인스턴스의 멤버를 기준으로 참조 변수의 타입의 멤버가 실제 인스턴스의 멤버 수보다 작은 것은 사용할 수 있는 기능을 줄이는 것이기에 허용
    • 그러나 그 반대는 참조하고 있는 인스턴스에 실제로 구현된 기능이 없으므로 사용이 불가하다
  • 메서드 오버라이딩 및 메서드 오버로딩 또한 다형성의 한 예시
    • 둘 다 같은 이름의 메서드를 재사용 또는 덮어쓰기로 다르게 사용
    • 이는 하나의 객체가 여러가지 형태를 가질 수 있는 성질인 다형성의 의미와 궤를 같이하는 것

참조 변수의 타입 변화

  • 참조 변수의 타입 변환은 사용할 수 있는 멤버의 개수를 조절하는 것
  • 타입 변환 시 충족되어야 할 조건
    1. 서로 상속 관계에 있는 상위 클래스 - 하위 클래스 사이에만 타입 변환 가능
    2. 하위 클래스 타입에서 상위 클래스 타입으로의 타입 변환(업 캐스팅)은 형변환 연산자(괄호)를 생략할 수 있다
    3. 상위 클래스에서 하위 클래스 타입으로 변환(다운 캐스팅)은 형변환 연산자(괄호)를 반드시 명시해야 한다
      • 다운 캐스팅은 업 캐스팅이 되어있는 참조 변수에 한해서 가능
class Animal {
    public void print() {
        System.out.println("Animal 클래스");
    }

    public sleep() {
        System.out.println("자는 중...");
    }
}

class Dog extends Animal {
    public void print() {
        System.out.println("Dog 클래스");
    }

    public void howl() {
        System.out.println("멍멍");
    }
}

class Cat extends Animal {
    public void print() {
        System.out.println("Cat 클래스");
    }

    public void play() {
        System.out.println("노는 중...");
    }
}

public class Main {
    public static void main(String[] args) {
        Dog dog = new Dog();
        Animal animal = (Animal)dog;
        Cat cat = (Cat)animal;
    }
}

instanceof 연산자

  • 참조 타입의 타입 변환, 즉 캐스팅이 가능하니 여부를 반환하는 문법 요소
  • 캐스팅 가능 여부 판단
    1. 객체를 어떤 생성자로 만들었는가
    2. 클래스 사이에 상속관계가 존재하는가
참조변수 instanceof 타입
  • 반환값이 true라면 검사한 타입으로 변환이 가능함을 뜻하고 반환값이 false라면 검사한 타입으로 변환이 불가함을 뜻한다
    • 참조 변수가 null이라면 false를 반환
class Animal {
    public void print() {
        System.out.println("Animal 클래스");
    }

    public sleep() {
        System.out.println("자는 중...");
    }
}

class Dog extends Animal {
    public void print() {
        System.out.println("Dog 클래스");
    }

    public void howl() {
        System.out.println("멍멍");
    }
}

class Cat extends Animal {
    public void print() {
        System.out.println("Cat 클래스");
    }

    public void play() {
        System.out.println("노는 중...");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal dog = new Dog();
        System.out.println(dog instanceof Object); // true
        System.out.println(dog instanceof Animal); // true
        System.out.println(dog instanceof Cat); // false
        System.out.println(dog instanceof Dog); // true
    }
}

추상화(Abstraction)

  • 객체의 공통적인 속성과 기능을 추출하여 정의하는 것
  • 상속 vs 추상화
    • 상속
      • 하위 클래스를 정의하는데 상위 클래스를 사용
    • 추상화
      • 기존 클래스들의 공통적인 요소를 뽑아서 상위 클래스를 만들어내는 것
  • 추상화의 장점
    • 공통적인 속성과 기능을 모아서 정의해주면
      1. 코드의 중복을 줄일 수 있다
      2. 보다 효과적으로 클래스 간의 관계를 설정할 수 있다
      3. 유지/보수가 용이해진다
  • 인터페이스 및 추상 클래스를 사용해 추상화를 구현할 수 있다

abstract 제어자

  • 자바의 제어자는 접근 제어자기타 제어자로 구분될 수 있는데, abstract 제어자는 기타 제어자 중 빈번하게 사용되는 제어자
  • abstract는 주로 클래스와 메서드를 형용하는 키워드로 사용된다
    • 추상 메서드(abstract method) : 메서드 앞에 abstract가 붙은 경우
    • 추상 클래스(abstract class) : 클래스 앞에 abstract가 붙은 경우
  • 어떤 클래스에 추상 메서드가 포함되어 있다면 해당 클래스는 자동으로 추상 클래스가 된다
abstract class AbstractExample {
    abstract void print();
}
  • 추상 메서드
    • 메서드의 시그니처만 존재하고 바디가 없는 메서드
    • abstract 키워드를 메서드 이름 앞에 붙여 해당 메서드가 추상 메서드임을 표시한다
  • 추상 클래스
    • 메서드 바디가 완성이 되기 전까지 이를 기반으로 객체 생성을 할 수 없다!

추상 클래스

  • 메서드 시그니처만 존재하고 바디가 선언되지 않은 추상 메서드를 포함하는 '미완성 설계도'
  • 미완성된 구조이므로 이를 기반으로 객체를 생성할 수 없다!
  • 추상 클래스는 왜 만드는가?
    1. 상속 관계에 있어 새로운 클래스를 작성하는데 유용
      • 메서드의 내용이 상속받는 클래스에 따라 종종 달라진다
      • 상위 클래스에 선언부만 작성하고, 실제 구현은 상속을 받는 하위 클래스에서 구현하도록 한다
      • 이에 따라, 설계하는 상황이 변하더라도 보다 유연하게 대응할 수 있다
      • 즉, 오버라이딩을 통해 추상 클래스로부터 상속받은 추상 메서드의 내용을 구현하여 메서드를 완성시킬 수 있고 이렇게 완성된 클래스를 기반으로 객체를 생성
    2. 추상 클래스는 추상화를 구현하는데 핵심적인 역할을 수행
      • 여러 사람이 개발하는 경우, 공통된 속성과 기능임에도 다른 변수와 메서드로 정의되어 오류가 발생할 수 있는데 이러한 오류를 미연에 방지할 수 있다
      • 상속계층도의 상층부에 위치할수록 추상화의 정도가 높고 아래로 내려갈수록 구체화된다

final 키워드

  • 필드, 지역 변수, 클래스 앞에 위치할 수 있으며 위치에 따라 의미가 조금씩 달라진다
    • 클래스 앞에 위치 : 변경 또는 확장이 불가능한 클래스, 상속을 할 수 없음
    • 메서드 앞에 위치 : 오버라이딩 불가
    • 변수 앞에 위치 : 값 변경이 불가한 상수
  • 변경이 불가능하고 확장할 수 없다는 점에서는 유사하다

인터페이스(Interface)

  • 두 개의 다른 대상 사이를 연결한다는 의미
  • 서로 다른 두 시스템/장치/소프트웨어 등을 이어주는 부분 또는 그런 접속 장치
  • 인터페이스 vs 추상 클래스
    • 공통점 : 추상화를 구현하는 데에 활용된다
    • 차이점 : 인터페이스는 추상 클래스에 비해 더 높은 추상성을 가진다
    • 추상 클래스
      • 메서드 바디가 없는 추상 메서드를 하나 이상 포함한다는 점 외에는 기본적으로 일반 클래스와 동일
    • 인터페이스
      • 기본적으로 추상 메서드와 상수만을 멤버로 가질 수 있음 -> 즉, 추상 클래스에 비해 추상화 정도가 더 높다고 할 수 있음
      • Java 8 이후로 default/static 메서드를 인터페이스에 포함시킬 수 있다

인터페이스의 기본 구조

public interface Car {
    public static final int wheelNum = 4;
    // final int wheelNum = 4;
    // static int wheelNum = 4;

    public abstract void ride();
    void powerUp();
}
  • interface 키워드를 사용하여 만들어지고 구현부가 완성되지 않은 추상 메서드와 상수만으로 구성된다
  • 내부의 모든 필드는 public static final로 정의되고, static/default 메서드를 제외하고는 모든 메서드는 public abstract로 정의됨
    • 모든 인터페이스의 필드와 메서드에는 위 요소가 내포되어 있으므로 일부분 또는 전부 생략 가능
      • 컴파일러가 자동으로 생략된 부분을 추가

인터페이스의 구현

  • 인터페이스는 그 자체로 인스턴스를 생성할 수 없고, 메서드 바디를 정의하는 클래스를 따로 작성해야 한다
    • 이 과정은 상속과 기본적으로 동일하지만, implements 키워드를 사용한다
class 클래스명 implements 인터페이스명 {
    ... // 인터페이스에 정의된 모든 추상 메서드를 구현
}
  • 특정 인터페이스를 구현한 클래스는 해당 인터페이스에 정의된 모든 추상 메서드를 구현해야만 한다!
    • 어떤 클래스가 특정 인터페이스를 구현한다
      • 해당 인터페이스가 가진 모든 추상 메서드들을 해당 클래스 내에서 오버라이딩하여 바디를 완성한다

인터페이스의 다중 구현

  • 상속
    • 단일 상속
    • 다중 상속 불가
      • 하위 클래스는 단 하나의 상위 클래스만 상속받을 수 있다
  • 인터페이스
    • 다중 구현 가능
      • 하나의 클래스가 여러 개의 인터페이스를 구현할 수 있다
    • 인터페이스는 인터페이스로부터만 상속이 가능
    • 클래스와 달리 Object 클래스와 같은 최고 조상이 존재하지 않음
class 클래스명 implements 인터페이스명1, 인터페이스명2, ... {
    ...
}
  • 인터페이스의 다중 구현이 가능한 이유
    • 다중 상속이 불가능한 이유
      • 부모 클래스에 동일한 이름의 필드 또는 메서드가 존재하는 경우 충돌이 발생
    • 인터페이스는 애초에 미완성된 멤버를 갖고 있으니 충돌이 발생할 여지가 없고, 따라서 다중 구현이 가능하다
  • 특정 클래스는 다른 클래스로부터 상속을 받으면서 동시에 인터페이스를 구현할 수 있다
interface Car {
    public abstract void powerUp();
}

abstract class Sedan {
    public abstract void fuelUp();
}

class Grandeur extends Sedan implements Car {
    public void powerUp() {
        System.out.println("시동 걸기");
    }

    public void fuelUp() {
        System.out.println("가솔린");
    }
}

class Malibu extends Sedan implements Car {
    public void powerUp() {
        System.out.println("시동 걸기");
    }

    public void fuelUp() {
        System.out.println("디젤");
    }
}

인터페이스의 장점

  • 기능이 가지는 역할과 구현을 분리시켜 사용자 입장에서는 복잡한 구현의 내용 또는 변경과 상관없이 해당 기능을 사용할 수 있다
    • 사용자로 하여금 복잡한 기능의 구현이나 교체/변경을 신경쓰기 않고 손쉽게 해당 기능을 사용할 수 있다
    • 개발자로 하여금 선언과 구현을 분리시켜 개발시간을 단축하고, 독립적인 프로그래밍을 통해 한 클래스의 변화가 다른 클래스에 미치는 영향을 최소화할 수 있다
profile
자바, 웹 개발을 열심히 공부하고 있습니다!

0개의 댓글