Polymorphism(다형성) - 여러 가지 형태를 가질 수 있는 능력을 의미 - 조상클래스 타입의 참조변수로 자손클래스의 인스턴스를 참조할 수 있도록 함 - 자손타입의 참조변수로 조상타입의 인스턴스를 참조할 수 없음
참조변수의 형변환 - 자손타입 → 조상타입 (Up-casting) : 형변환 생략가능 - 자손타입 ← 조상타입 (Down-casting) : 형변환 생략 불가능 - 형변환은 참조변수의 타입을 변환하는 것이지 인스턴스를 변환하는 것은 아니기 때문에 참조변수의 형변환은 인스턴스에 아무런 영향을 미치지 않음 - 단지 참조변수의 형변환을 통해서, 참조하고 있는 인스턴스에서 사용할 수 있는 멤버의 범위를 조절하는 것뿐임
Animal을 bird로 선언했기 때문에 Animal에 있는 것은 모두 사용이 가능하나 wings는 되지 않는다 Bird에서 wing을 사용하고 싶다면 강제로 지정을 해주자
생성자를 만들고 코드를 더 추가해 봅시다.
만들어 주고 각각 값을 넣어 출력해 주면
잘 나옵니다.
이렇게 추가해주고 실행을 해보면
실행은 잘 되는데 animal1로 바꿔도 빨간 값은 똑같다. monkey1의 메모리 주소가 animal1로 들어갔기 때문이다.
한번 더 확인해 보자
역시 똑같게 나온다. 보이는 건 다르게 보이지만 데이터 값은 그 안에 다 있습니다.
instanceof연산자 - 참조변수가 참조하고 있는 인스턴스의 실제 타입을 알아보기 위해 instanceof연산자를 사용 - 주로 조건문에서 사용됨 - instanceof 왼쪽에는 참조변수, 오른쪽에는 타입이 피연산자로 위치 - 연산의 결과 true, false의 boolean 값으로 반환 - 어떤 타입에 대한 instanceof연산의 결과가 true라는 것은 검사한 타입으로 형변환이 가능하다는 것을 뜻함
심플하게 코드로 확인해 보면
true가 뜹니다. Monkey는 extends 해줬기 때문이죠
참조변수와 인스턴스의 연결 - 조상 클래스에 선언된 멤버변수와 같은 이름의 인스턴스변수를 자손 클래스에 중복으로 정의했을 때, 조상타입의 참조변수로 자손 인스턴스를 참조하는 경우와 자손타입의 참조변수로 자손 인스턴스를 참조하는 경우는 서로 다른 결과를 얻음 - 메서드의 경우 조상 클래스의 메서드를 자손 클래스에서 오버라이딩한 경우에도 참조 변수의 타입에 관계없이 항상 실제 인스턴스의 메서드(오버라이딩된 메서드)가 호출되지만, 멤버변수의 경우 참조변수의 타입에 따라 달라짐
abstract class - 추상클래스 - 클래스가 설계도라면 추상클래스는 미완성 설계도 - 추상클래스로는 인스턴스를 생성할 수 없고 상속을 통해서 자손클래스에 의해서만 완성됨 - abstract 키워드 사용 - 추상 메서드를 포함하고 있다는 것을 제외하고는 일반클래스와 전혀 다르지 않음
추상메서드 - 선언부만 작성하고 구현부는 작성하지 않은 메서드 - 상속받는 클래스에 따라 기능이 달라질 수 있기 때문에 조상 클래스에서는 선언부만 작성 - 주석을 덧붙여 어떤 기능을 수행할 목적으로 작성되었는지 알려줌 - abstract 키워드를 붙이고 {} 대신 ; 사용
클래스를 새로 하나 만들어 줍시다. Animal은 같은 클래스에 남아있기 때문에 에러가 납니다. 따라서 2를 붙였습니다.
eat은 추상메서드입니다.
Animal2를 상속받았는데 에러가 뜬다. 상속했는데 eat을 구현 안 해서 에러가 뜬다.
단순하게 나옵니다.
이렇게도 사용이 가능합니다. eat을 Animal2도 가지고 있기 때문에 사용이 가능기 때문.
Interface - 일종의 추상클래스 - 추상클래스보다 추상화 정도가 높아 일반 메서드 또는 멤버변수를 가질 수 없음
인터페이스 작성 - class 대신 interface 키워드를 사용, 접근제어자 사용 가능 참고용 사진 인터페이스의 제약사항 1. 모든 멤버변수는 public static final 이어야 하며, 생략 불가능 2. 모든 메서드는 public abstract 이어야 하며, 생략 가능
또한 - 일반 메서드, 멤버 변수는 사용 불가능 - static final로 선언된 상수 / abstract로 선언된 추상 메서드만 가질 수 있음.
이때까지는 class를 만들었지만 interface를 만들어봅시다.
new -> Interface
int number;을 치면 에러가 뜹니다. 빨간 부분을 보고 클릭하면
이렇게 바뀔 거다. 인터페이스에서는 static final로 선언된 상수만 선언 가능하다.
이렇게도 되지 않는다. 앞에 default나 static을 붙이거나 method body를 없애야 사용이 가능합니다.
구현부가 없으면 추상으로 인식하고 인터페이스에서는 추상메서드만 선언 가능하다.
인터페이스의 상속 - 인터페이스는 인터페이스로부터만 상속받을 수 있으며 다중상속 가능
이렇게 가능하다. 역시 상속이라 extends를 해준다.
클래스는 무조건 하나만 상속받을 수 있는데 인터페이스는 다중상속이 가능하다.
인터페이스의 구현 - 인터페이스는 그 자체로 인스턴스를 생성할 수 없음 - 인터페이스를 구현하는 클래스를 작성해야 인스턴스 생성 가능 인스턴스를 반환한다는 것을 의미 - implements 키워드 사용
구현을 해보자
@override가 없어도 된다.
또한, 인터페이스에 작성된 추상메서드는 반드시 모두 구현해야 함.
인터페이스를 이용한 다형성 - 인터페이스타입의 참조변수로 구현한 클래스의 인스턴스를 참조할 수 있고 형변환도 가능 - 반환타입이 인터페이스라는 것은 메서드가 해당 인터페이스를 구현한 클래스의 인스턴스를 반환한다는 것을 의미
아까의 다형성을 생각해보자 똑같이 적용시킬 수 있다. 이때까지 코드는 한 interface안에서 작성했다.
인터페이스의 장점 1. 개발시간을 단축시킬 수 있음 - 인터페이스가 작성되면, 메서드를 호출하는 쪽에서 메서드의 선언부만 알고도 프로그램 작성이 가능 - 인터페이스를 구현하는 클래스를 작성하면, 인터페이스를 구현하는 클래스가 작성될 때까지 기다리지 않고 양쪽에서 개발이 가능
2. 표준화가 가능 - 프로젝트에 사용되는 기본 틀을 인터페이스로 작성하여 일관되고 정형화된 프로그램 개발이 가능 - 서로 관계없는 클래스들에게 관계를 맺어줄 수 있음 - 상속관계에 있지 않고, 같은 조상을 가지고 있지 않아도 하나의 인터페이스를 공통적으로 구현해 관계를 맺음
3. 독립적인 프로그래밍이 가능 - 클래스의 선언과 구현을 분리시킬 수 있기 때문에 실제구현에 독립적인 프로그램 작성이 가능 - 클래스 간을 인터페이스를 이용해 간접적인 관계로 만들면, 변경한 클래스가 다른 클래스에 영향을 미치지 않음