상속
자바에서 상속은 부모 클래스의 변수와 메서드를 물려받는 것을 말합니다. 상속을 통한 장점은 코드의 재사용성을 통해 코드의 간결성을 확보한다는 것입니다.
코드상으로 상속을 표현하면 다음과 같습니다.
class 자식 클래스명 extends 부모 클래스명{
}
기본적으로 상속을 받은 자식 클래스는 부모 클래스의 변수와 메서드를 모두 사용할 수 있지만 모두 다 사용을 할 수는 없습니다.
한 예시로 접근 지정자에 따라 상속하여도 사용할 수 없는 변수 및 메서드 들이 생겨나기 때문입니다(...흠 아직 잘 모르겠음)
// https://blog.naver.com/swoh1227/222174170682
온라인 자바 스터디#5 - 클래스(클래스, 메소드, 생성자, this/super키워드)
유튜브를 통해 자바 스터디를 진행하는 프로그램이 있어 한번 정리해볼까 합니다. (아래는 해당 링크이니 ...
blog.naver.com
자바 상속의 특징
다음은 자바 상속의 특징을 알아보겠습니다.
첫번째 상속은 단일 상속만 가능합니다.
예를 들어 다음의 코드처럼은 상속이 불가능합니다.
class 자식 클래스명 extends 부모 클래스명1, 부모 클래스명2{
}
두번째 자바의 계층 구조 최상위에는 java.lang.Object 클래스가 존재합니다.
세번째 상속의 횟수를 제한두지 않는 다는 것입니다.
class A{};
class B extends A{};
class C extends B{}
이처럼 자식 클래스들간의 상속을 계속해서 받을 수 있습니다.
네번째 부모의 메소드와 변수만 상속되며, 생성자는 상속되지 않습니다.
여기서 메서드는 부모의 메서드를 오버라이딩하여 재정의해 사용할 수 있습니다.
super 키워드
super의 경우 this와는 다르게 자식 클래스가 부모 클래스로 부터 상속을 받아 멤버로 사용하고자 할 때 사용됩니다.
class Parent{
int a = 10;
}
class Child extends Parent{
int a = 20;
void childMethod(){
System.out.println(a);
System.out.println(this.a);
System.out.println(super.a);
}
}
해당 코드를 보면 this.a는 Child 클래스 자신의 a 인스턴스 변수를 가져와 출력하지만 super.a는 부모 클래스에 있는 a 인스턴스 변수를 가져와 출력합니다.
여기서 super는 상속의 특징 중 네번째 기능을 수정하기 위해 사용되었습니다.
메소드 오버라이딩(Overriding)
상속은 부모의 메서드를 가져다 사용할 수도 있지만 이를 가져와 재정의해 사용할 수도 있습니다.
다음의 코드를 보겠습니다.
public class A{
public void print(){
}
}
public class B extends A{
// 메서드 오버라이딩 - A를 상속받았으나 함수를 재정의하였음
public void print(){
System.out.println("B");
}
}
public class Test{
@Test
void 오버라이딩_테스트(){
B b = new B();
b.print();
}
}
위의 코드를 실행했을 때 나와야할 출력 결과는 class에서 재정의한 B입니다.
오버라이딩은 위와 같이 부모의 함수를 재정의하는 기능임으로 함수명, 리턴값, 파라미터가 모두 동일해야 합니다.
오버라이딩과 비슷한 기능으로 오버로딩이 있습니다. 이는 함수명, 리턴값은 같지만 파라미터의 인자값을 달리해 메서드로 사용할 수 있는 기능을 말합니다.
다이나믹 메소드 디스패치 (Dynamic Method Dispatch)
메소드 디스패치는 어떤 메소드를 호출할지 결정하여 실제로 실행시키는 과정을 말합니다.
이런 메소드 디스패치에는 정적 메소드 디스패치, 동적 메소드 디스패치, 더블 디스패치가 있습니다.
정적 메소드 디스패치(static Method Dispatch)
위에서 작성한 오버라이딩 소스를 보면 Test함수에서 b.print()를 호출했을 때 우리는 클래스B의 오버라이딩 된 함수가 불릴 것을 알고 있었습니다.
우리가 알고 있는 것과 같이 컴파일러 역시 B class 안에 있는 print를 호출하고 실행해야 된다는 것을 명확히 알고 있는데, 이를 정적 메소드 디스패치라고 합니다.
동적 메소드 디스패치(Dynamic Method Dispatch)
동적 메소드 디스패치는 정적 메소드 디스패치와는 다르게 컴파일러가 어떤 메소드를 호출해야되는지 모르는 것을 말합니다.
class ParnetA{
private BB bb;
A(BB bb){
this.bb = bb;
}
void print(){
bb.print();
}
}
interface BB{
void print();
}
class ChildB implements BB{
public void print(){
System.out.println("B");
}
}
class ChildB1 implements BB{
public void print(){
System.out.println("B1");
}
}
public class Test{
@Test
void 오버라이딩_테스트2(){
ParnetA a = new ParnetA(new ChildB());
a.print();
}
}
위의 코드는 A class가 멤버 변수인 bb를 가지고 print 함수를 실행합니다.
그리고 B class와 B1 class는 각각 BB 인터페이스의 상속을 받아 메서드를 구현하고 있습니다.
이렇게 되면 A, B, B1, class가 컴파일 되는 시점엔 A 생성자의 BB타입으로 어떤 값이 들어 오는지 알 수 없게 됩니다.
파라미터 안에서 실행되는 객체가 무엇인지 알기 위해서는 A생성자를 사용하는 "오버라이딩_테스트" 메서드에서 부터 알 수 있게 됩니다.
이를 통해 알 수 있는 건 컴파일 시점엔 소스상 표시 된 BB 타입 만을 알 수 있고, 구현을 시작하는 런타임 시점에 어떤 객체의 print 함수를 실행하는지 알 수 있게 됩니다.
이처럼 동적 메소드 디스패치는 컴파일 시점에 모든 객체와의 연결을 진행하는 것이 아니라 실행 시점에 객체의 실행을 결정하는 메소드 실행 방법을 말합니다.
더블 디스패치(Double Dispatch)
더블 디스패치는 동적 메소드 디스패치를 두번하는 것을 의미합니다.
interface Post{
void postOn(SNS sns);
}
class Text implements Post{
@Override
public void postOn(SNS sns){
sns.post(this);
}
}
class Picture implements Post{
@Override
public void postOn(SNS sns){
sns.post(this);
}
}
interface SNS{
void post(Text text);
void post(Picture picture);
}
class Facebook implements SNS{
@Override
public void post(Text text){
System.out.println("Text: facebook");
}
@Override
public void post(Picture picture){
System.out.println("Picture: facebook");
}
}
class Twitter implements SNS{
@Override
public void post(Text text){
System.out.println("Text: twitter");
}
@Override
public void post(Picture picture){
System.out.println("Picture: twitter");
}
}
@Test
void test(){
List<Post> posts = Arrays.asList(new Text(), new Picture());
List<SNS> sns = Arrays.asList(new Facebook(), new Twitter());
posts.forEach(p -> sns.forEach(s -> p.postOn(s)));
}
더블 디스패치에 대한 예제입니다. 해당 소스에선 두번의 동적 디스패치가 이루어집니다.
첫번째는 Post에서 어떤 구현체의 postOn을 사용할지에 대한 내용이 정해지고, 두번째는 postOn에서 SNS의 어떤 구현체의 post 함수를 사용할지 정해지게 됩니다.
이렇게 두번의 동적 디스패치를 거치게 되면 구현체를 생성하는 것에 자유로워지게 됩니다.
방문자 패턴
방문자 패턴이란 알고리즘을 객체 구조로 분리시켜 디자인하는 패턴을 말합니다. 주로 데이터와 메소드를 구분하기위해 사용합니다. 이런 방문자 패턴은 더블 디스패치를 이용한 대표적인 패턴이라고 할 수 있습니다.
방문자 패턴에 대한 설명을 SNS 코드로 설명해보겠습니다.
위의 Text와 Picture은 Element A와 Element B에 속하게 됩니다. 그리고 이들의 함수인 postOn은 Visitor인 SNS에 의해 각 오브젝트에 post됩니다. 이처럼 데이터인 Element A와 Element B가 Visitor인 SNS에 의해 데이터의 처리를 모두 맡기는 것을 방문자 패턴이라고 부릅니다.
추가하고 싶은점
장점은 무엇이고 단점은 무엇일까?
어떤 형태로 적용했을 때 가장 좋은 방식이 될까?
추상 클래스
추상 클래스는 클래스를 만들기 위한 일종의 설계도로 인스턴스를 생성할 수 없는 클래스를 말합니다. 이를 사용하기 위해선 반드시 자식 클래스에서 상속을 받아 클래스를 모두 구현해야 합니다.
// 추상 클래스 선언 방법
abstract class 클래스 이름{
}
이런 추상클래스는 반드시 하나 이상의 추상 메서드를 포함하며, 생성자와 멤버변수, 일반 메서드 모두를 가질 수 있습니다.
추상 메서드
abstract 리턴타입 메서드이름();
추상 클래스는 메서드의 선언부만 작성하고 구현부는 미완성한채로 두는 메소드를 말합니다. 추상 클래스는 보통 주석을 통해 어떤 기능을 수행하는지 알려주고, 구현부는 각각 상속을 받는 자식 클래스마다 다르게 구현됩니다. 자식 클래스가 구현을 한다는 특징때문에 접근 지정자를 private로 설정할 순 없습니다.
인터페이스
인터페이스는 추상 클래스의 일종으로 추상 클래스보다는 추상화 정도가 높아 일반 메서드나 멤버 변수를 가질 수 없습니다. 오직 추상 메소드와 상수만을 멤버로 가질 수 있는 인터페이스입니다.
interface 인터페이스이름{...}
interface B{...}
interface C{...}
interface D{...}
class K{}
// 인터페이스는 다중 상속이 가능
interface A extends B, C, D{
}
// 구현한다는 의미로 사용
class A extends K implements B, C{...}
인터페이스의 모든 멤버 변수는 public static final이어야 하며, 이를 생략할 수도 있습니다. 또한 메서드 역시 모두 public abstract이어야 하며, 생략이 가능합니다.
이런 인터페이스는 같은 인터페이스 끼리만 상속이 가능하며, 상속과는 다르게 다중 상속이 가능합니다.
인터페이스를 이용한 다형성
public interface MyInterface{
int value = 10;
int calculate(int a);
}
public class CalA implements MyInterface{
@Override
public int calculate(int a){
return value + a;
}
}
public class CalB implements MyInterface{
@Override
public int calculate(int a){
return value * a;
}
}
public static final로 선언된 value 인스턴스를 상속받은 CalB 클래스에서 계산을 하는 과정에서 사용하고 있습니다.
// 다형성 예제
MyInterface add = new CalA();
MyInterface multiply = new CalB();
int calVal = 20;
System.out.println(add.calculate(calVal));
System.out.println(multiply.calculate(calVal));
final 키워드
final 키워드는 엔티티를 한번만 할당하겠다는 의미로 자바에서는 총 세가지 의미로 사용됩니다.
final 변수
final 변수는 우리가 흔히 아는 상수를 의미하며, 생성자나 대입연산자를 통해 한번만 초기화가 가능한 변수입니다.
final 메서드
final 메서드는 오버라이드하거나 숨길 수 없음을 의미합니다.
final 클래스
해당 클래스를 상속할 수 없음을 의미하며, 상속을 할 수 없기에 상속 계층에서 마지막 클래스라는 의미를 가지고 있습니다.
Object 클래스
java.lang.Object 클래스는 모든 클래스의 최상위 클래스를 말합니다.
메소드 | 설명 |
---|---|
boolean equals(Object obj) | 두 객체가 같은 지 비교한다.(같으면 true, 틀리면 false) |
String toString() | 객체의 문자열을 반환한다. |
protected Object clone() | 객체를 복사한다. |
protected void finalize() | 가비지 컬렉션 직전에 객체의 리소스를 정리할때 호출한다. |
Class getClass() | 객체의 클레스형을 반환한다. |
int hashCode() | 객체의 코드값을 반환한다. |
void notify() | wait된 스레드 실행을 재개할 때 호출한다. |
void notifyAll() | wait된 모든 스레드 실행을 재개할 때 호출한다. |
void wait() | 스레드를 일시적으로 중지할 때 호출한다. |
void wait(long timeout) | 주어진 시간만큼 스레드를 일시적으로 중지할 때 호출한다. |
모든 클래스는 정의할 때부터 명시적으로 java.lang.Object 클래스를 상속받게 됩니다. 그러므로 위의 정의된 Object의 함수들은 어떤 클래스에서도 호출이 가능합니다. |
출처
'JAVA' 카테고리의 다른 글
백기선 자바 스터디 8주차 (1) | 2023.08.31 |
---|---|
백기선 자바 스터디 4주차 (0) | 2023.07.18 |
백기선 자바 스터디 3주차 (0) | 2023.07.12 |
백기선 자바 스터디 2주차 (0) | 2023.06.26 |
백기선 자바 스터디 1주차 (0) | 2023.06.22 |