메시지와 인터페이스 (chapter6)


  • 객체지향적 프로그램 을지향 ->
    • 클래스가 아니라 객체에 초점 ->
      • 구체적으로 객체의 책임에 초점

중요한 것은 책임이 객체가 수신할 메시지의 기반이 된다는 것


협력과 메시지

  • 객체는 서로 협력의 관계에 있고 메세지를 주고 받는다!! (Chapter3)
  • 메시지는 객체 사이의 매개체이다
    • 즉 유일한 접근 방법은 메세지

클라이언트 - 서버 모델

  • 두 객체 사이의 협력 관계를 설명하기 위한 전통적인 방법 클라이언트-서버모델
  • 협력안에서 메세지 전송쪽을 클라이언트라 지칭
  • 협력안에서 메세지 수신쪽을 서버라 지칭
    협력은 클라이언트가 서버의 서비스를 요청하는 단방향 상호작용

  • Screening은 메시지를 전송하는 클라이언트, Movie는 메시지를 수신하는 서버

  • Movie은 메시지를 전송하는 클라이언트, DiscountPolicy는 메시지를 수신하는 서버

  • 이처럼 객체는 클라이언트와 서버 모두의 역할을 수행할 수 있다

메세지 구조

메시지와 메서드

  • 메시지 전송자는 어떤 메시지를 전송할지, 메시지 수신자는 어떤 메시지를 수신할지만 알면 된다. (즉, 어떠한 클래스가 전송하는지, 어떠한 클래스가 수신하는지 몰라도 된다)

condition의 경우 PeriodCondition, SequenceCondition 인스턴스가 올 수 있다. 컴파일시점과 실행시점의 인스턴스가 다르기 때문에, 이러한 구조의 메커니즘은 두 객체 사이의 결합도를 낮춤으로써 유연하고 확장 가능한 코드를 작성할 수 있게 만든다.

용어 정리

  • 메시지
    • 객체간 협력을 위해 사용하는 의사소통 메커니즘
  • 오퍼레이션
    • 객체가 다른 객체에게 제공하는 추상적인 서비스
  • 메서드
    • 메시지에 응답하기 위해 실행되는 코드 블록. 즉, 메서드는 오퍼레이션의 구현이다
  • 퍼블릭 인터페이스
    • 객체가 협력에 참여하기 위해 외부에서 수힌할 수 있는 메시지의 묶음, 집합이다
  • 시그니처
    • 시그니처는 오퍼레이션의 이름과 파라미터를 합쳐 부르는 말이다

인터페이스와 설계 품질

인터페이스 설계 품질을 위해 다음과 같은 원칙을 고려해보자

  • 디미터 법칙
  • 묻지 말고 시켜라
  • 의도를 드러내는 인터페이스

디미터 법칙

“낯선 자에게 말하지 말라” - [Larman]
“오직 인접한 이웃하고만 말하라” - [Metz]

  • 객체 내부 구조에 강하게 결합되지 않도록 협력 경로를 제한하라
  • ‘오직 하나의 .(도트)를 이용하라’ 라고 요약 되기도 한다. 아래는 위반되는 코드이다.
    screening.getMovie().getDiscountCondition();

즉, 디미터 법칙은 객체가 객체 자신이 자율적 존재가 되어야 함을 강조한다

묻지 말고 시켜라

  • 객체의 상태에 관해 묻지 말고 원하는 것을 시켜라. 이런 스타일의 메시지 작성을 장려하는 원칙이다.
  • 메시지 전송자는 메시지 수신자의 상태를 기반으로 결정을 내린 후 메신지 수신자의 상태를 바꿔서는 안된다.
  • 이 원칙을 따르면, 자연스럽게 정보 전문가에게 책임을 할당하게 되고 높은 응집도를 가진 클래스를 얻을수 있다.

의도를 드러내는 인터페이스

  • 메서드를 명명하는 두가지 방법
    첫번째, 메서드가 작업을 어떻게 수행할지 나타내도록 이름을 지어라

다만 이런 스타일은 좋지 않다

public class PeriodCondition {
    public boolean isSatisfiedByPeriod(Screening screeing) {}
}

public class SequenceCondition {
    public boolean isSatisfiedBySequence(Screening screeing) {}
}
  • 메서드에 대해 제대로된 커뮤니케이션이 안된다. 두가지 모두 할인 조건을 판단하는 작업을 수행하지만, 두 메서드의 이름이 다르기 때문에 내부 구현을 잘 모른다면 두 메서드가 동일한 작업을 수행한다는 사실을 알기 어렵다
  • 더 큰 문제는 메서드 수준에서 캡슐화를 위반한다. 할인 여부를 판단하는 방법이 변경된다면 메서더의 이름 역시 바뀌어야 할 것이다.

두번째, ‘어떻게’가 아니라 ‘무엇’을 하는지를 드러내라

public class PeriodCondition implements DiscountCondition {
    public boolean isSatisfied(Screening screeing) {}
}

public class SequenceCondition implements DiscountCondition {
    public boolean isSatisfied(Screening screeing) {}
}
  • 동일한 목적을 수행한다는 것을 명시하기 위해 메서드 이름을 통일하였다
  • 다만, 동일한 계층의 타입으로 묶어 주어야 한다. 인터페이스를 이용하자

이처럼, 어떻게 하느냐가 아니라 무엇을 하느냐에 초점을 맞추어 보자. 이런식으로 메서드의 이름을 통일하면 의도를 드러낼 수 있고, 인터페이스를 이용해 유연한 설계가 가능해진다.


원칙의 함정

디미터법칙, 묻지 말고 시켜라 법칙 등의 법칙이 깔끔하게 유연한 설계를 도와주지만 절대적인 것은 아니다. 잊지 말아야 할 것은 설계는 트레이드오프의 산물이라는 점이다.

디미터 법칙은 하나의 도트(.)를 강제하는 규칙이 아니다

IntStream.of(1,2,3).filter(x -> x >10).distinct().count();

dot을 한줄에 여러개 사용하였다.
디미터 법칙은 결합도와 관련된 것이다. 결합도는 객체의 내부 구조가 외부로 노출되는 경우를 한정하기 때문에 디미터법칙을 위반하지 않는다.

public class PeriodCondition implements DiscountCondition {
    public boolean isSatisfiedBy(Screening screening) {
        return screening.getStartTime().getDayOfWeek().equals(dayOfWeek) && startTime.compareTo(screeing.getStartTime().toLocalTime()) <= 0 && endTime.compareTo(screeing.getStartTime().toLocalTime()) >= 0;
    }
}

해당 로직을 묻지도 말고 시켜라 원칙을 적용해, Screening으로 옮겨 보자

public class Screeing {
    public boolean isDiscountable(DayOfWeek dayOfWeek, LocalTime startTime, LocalTime endTime) {
        return whenScreened.getDayOfWeek().equals(dayOfWeek) && startTime.compareTo(whenScreened.toLocalTime()) <= 0 && endTime.compareTo(whenScreened.toLocalTime()) >= 0;
    }
}

public class PeriodCondition implements DiscountCondition {
    public boolean isSatisfiedBy(Screening screening) {
        return screening. isDiscountable(dayOfWeek, startTime, endTime);
    }
}

묻지도 말고 시켜라 원칙을 지켰다. 그러나, Screening이 할인 조건을 떠맡게 되었다. 하지만 Screening 객체가 할인 조건을 책임 지는게 맞는 것일까? 당연히 그렇지 않다. 객체의 응집도가 낮아지게 되었다.

소프트웨어는 트레이드오프의 산물이기에 설계에 있어서 절대적인 법칙은 존재하지 않는다.


명령-쿼리 분리 원칙

명령 쿼리 분리 원칙은 퍼블릭 인터페이스에 오퍼레이션을 정의할 떄 참고할 지침을 제공한다.

  • 루틴

    • 어떤 절차를 묶어 호출 가능하도록 이름을 부여한 것
    • 프로시저와 함수로 구분된다
    • 프로시저
      • 부수효과를 발생시키지만 값을 반환할 수 없다
    • 함수
      • 값을 반환할 수 있지만 부수효과를 발생시킬 수 없다
  • 명령

    • 객체의 상태를 수정하는 오퍼레이션
  • 쿼리

    • 객체와 관련된 정보를 반환하는 오퍼레이션

중요한 것은 어떤 오퍼레이션도 명령이거나 동시에 쿼리여서는 안된다. 따라서 명령과 쿼리를 분리하기 위해서 다음의 두가지 규칙을 준수해야 한다

  1. 객체의 상태를 변경하는 명령은 반환값을 가질 수 없다
  2. 객체의 정보를 반환하는 쿼리는 상태를 변경할 수 없다

아래의 예시를 살펴보자

public class Event {
    public boolean isSatisFied(RecurringSchedule schedule) {
        if (...) {
            reschedule(schedule);
            return false;
        }
        return true;
    }
}

boolean이라는 상태값을 반환하지만, 중간에 reschedule을 호출한다, 그리고 schedule을 바꾸어 버린다 … 이 코드를 다음과 같이 분리해보자

public class Event {
    public boolean isSatisFied(RecurringSchedule schedule) {
        if (...) {
            return false;
        }
        return true;
    }
}

if (!event.isSatisfied(schedule)) {
    event.reschedule(schedule);
}    

명령과 쿼리를 분리함으로써 내부적인 버그를 해결할 수 있다.


참조

오브젝트 - 코드로 이해하는 객체지향 설계, 6장 메시지와 인터페이스

'Books > Object' 카테고리의 다른 글

[Object] 의존성 관리하기  (0) 2021.06.10
[Object] 객체 분해  (0) 2021.05.29
[Object] 메시지와 인터페이스  (0) 2021.05.25
[Object] 책임 할당하기  (0) 2021.05.16
[Object] 설계 품질과 트레이드오프  (0) 2021.05.08
[Object] 역할, 책임, 협력  (0) 2021.05.03
블로그 이미지

사용자 yhmane

댓글을 달아 주세요

Item20

추상 클래스보다는 인터페이스를 우선하라

자바가 제공하는 다중 구현 메커니즘으로 인터페이스와 추상클래스가 존재한다.

자바8부터 인터페이스에 default method를 지원하여 두 메커니즘 모두 인스턴스 메서드를 구현 형태로 제공할 수 있다.

둘의 가장 큰 차이는 추상클래스가 정의한 타입을 구현하는 클래스는 반드시 추상 클래스의 하위 클래스가 되어야 한다는 점이다.반면, 인터페이스는 어떤 클래스를 상속했든 같은 타입으로 취급 받는다.

  • 인터페이스 - 다중 상속이 가능하고 구현한 클래스와 같은 타입으로 취급음. Java8 부터 default 메서드 제공
  • 추상클래스 - 다중 상속이 불가하고, 구현체와 상하관계에 있

인터페이스 장점

기존 클래스에도 손쉽게 새로운 인터페이스를 구현할 수 있다

  • 인터페이스 - 인터페이스의 추상 메서드를 추가하고, 클래스에 implements 구문을 추가하여 구현체임을 알린다.
  • 추상클래스 - 계층 구조상 두 클래스의 공통 조상이어야 하며, 새로 추가된 추상 클래스의 모든 자손이 상속하게 된다.

믹스인 정의에 적합하다.

  • 추상 클래스는 단일 상속만 가능하기 때문에 기존 클래스에 덧씌울 수 없다.

인터페이스로는 계층구조가 없는 타입 프레임워크를 만들 수 있다.

먼저 인터페이스로 살펴보자.

public interface Singer {
    AudioClip sing(Song s);
}

public interface SongWriter {
    Song compose(int charPosition);
}

public interface SingSongWriter extends Singer, SongWriter {
    AudioClip strum();
    void actSensitive();
}

다음으로 추상클래스로 살펴보자.

public abstract class Singer {
    abstract AudioClip sing(Song s);
}

public abstract class SongWriter {
    abstract Song compose(int charPosition);
}

public abstract class SingerSongWriter {
    abstract AudioClip sing(Song s);
    abstract Song compose(int charPosition);
    abstract AudioClip strum();
    abstract void actSensitive();
}

추상 클래스로 만들면 다중상속이 불가하여 새로운 추상클래스를 만들어서 클래스 계층을 표현할 수 밖에 없다.

따라서 이 계층구조를 만들기 위해서는 많은 조합이 필요하게 된다.


디폴트 메서드 제약

자바8부터 인터페이스에서도 메서드를 구현할 수 있게 되었다. default 메서드. 다만, 아래와 같은 규칙을 지켜줘야 한다.

  • @implSpec 자바독 태그를 붙여 사용하려는 default 메서드를 문서화한다.
  • equals와 hashCode는 default 메서드로 제공해서는 안된다.
  • 인스턴스 필드를 가질 수 없다.
  • public이 아닌 정적 멤버를 가질 수 없다.
  • 만들지 않은 인터페이스에는 디폴트 메서드를 추가할 수 없다.

추상 골격 클래스(Skeletal Implementation)

인터페이스로는 타입을 정의하고 디폴드 메서드도 제공한다. 골격 구현 클래스는 나머지 메서드들까지 구현한다.

이렇게 해두면 단순히 골격 구현을 확장하는 것만으로 인터페이스를 구현하는데 필요한 일이 대부분 완료된다.(템플릿 메서드 패턴
)

시뮬레이트한 다중 상속(Simulated Multiple Inheritance)

골격 구현 클래스를 우회적으로 이용하는 방식이다.
인터페이스를 구현한 클래스에서 골격구현을 확장한 private 내부 클래스를 정의하고 각 메서드 호출을 내부 클래스의 인스턴스에 전달하는 것이다.

아이템 18에서 다룬 내용과 비슷한 방식이다.

public class ForwardingSet<E> implements Set<E> {
  private final Set<E> s;

  public ForwardingSet(Set<E> s) {
    this.s = s;
  }

  public void clear() {
    s.clear();
  }
  ... 중략 ...
}

골격 구현 작성방법

  • 먼저 인터페이스를 잘 살펴 다른 메서드들의 구현에 사용되는 기반 메서드를 선정한다
  • 이 기반 메서드들을 사용해 직접 구현할 수 있는 메서드들을 모두 디폴트 메서드로 제공한다.
  • 기반 메서드나 디폴트 메서드로 만들지 못한 메서드가 남아있다면, 이 인터페이스를 구현하는 골격 구현 클래스를 하나 만들어 남은 메서드들은 작성해 넣는다.

다음과 같이 구현한다.

public abstract class AbstractMapEntry<K,V> implements Map.Entry<K,V> {
    @Override public V setValue(V value) {
        throw new UnsupportedOperationException();
    }

    @Override public boolean equals(Object o) {
        if (o == this) {
            return true;
        }

        if (!(o instanceof Map.Entry)) {
            return false;
        }

        Map.Entry<?,?> e = (Map.Entry) o;
        return Objects.equals(e.getKey(),   getKey()) && Objects.equals(e.getValue(), getValue());
    }

    @Override public int hashCode() {
        return Objects.hashCode(getKey()) ^ Objects.hashCode(getValue());
    }

    @Override public String toString() {
        return getKey() + "=" + getValue();
    }
}

결론

  • 일반적으로 다중 구현용 타입으로는 인터페이스가 가장 적합하다.
  • 복잡한 인터페이스라면 구현하는 수고드를 덜어주는 골격 구현을 고려해보자.

참조

  • effective java 3/e chapter20
블로그 이미지

사용자 yhmane

댓글을 달아 주세요

직렬화

객체를 메모리, 데이터베이스, 혹인 파일로 저장하려면 어떻게 해야 할까? 이럴 때 필요한 것이 직렬화입니다.

  • 직렬화는 Serialization 이라고도 합니다.
  • 인스턴스의 상태를 그대로 저장하거나 네트웍으로 전상하고 이를 다시 역직렬화.복원(Deserialization) 하기도 합니다.

Serializable

  • 직렬화를 하려면 선언을 해주어야 합니다.
  • java.io.Serializable 인터페이스를 구현한 클래스의 객체만 직렬화 할 수 있습니다.
  • 직렬화 하지 말아야 할 값이 있다면 transient 키워드를 붙여줍니다.
    class Student implements Serializable {
    ...
    String name;
    transient String code;
    ...
    }
  • Serializable 인터페이스를 구현한 클래스는 자신의 serialVersionUID를 명시적으로 선언할 수 있습니다.
  • serialVersionUID는 필드의 이름이며 static, final이어야 하고 long type입니다. 선정하지 않으면 default 값이 선정됩니다.
    • class의 내용이 바뀌면 UID값 역시 바뀌게 되는데 네트웍 상황에서 값이 바뀌는 것을 방지하고자 사용합니다.

Serializable 사용해보기

class Student implements Serializable {
    private static final long serialVersionUID = 1L;
    private String name;
    private Integer grade;
    transient private Integer code;

    public Student(String name, Integer grade, Integer code) {
        this.name = name;
        this.grade = grade;
        this.code = code;
    }

    public String toString() {
        return "name : " + name + ", grade : " + grade + ", code : " + code;
    }
}

public class Test {
    public static void main(String[] args) {
        Student student1 = new Student("홍길동", 2, 4885);
        Student student2 = new Student("아무개", 3, 7531);
        System.out.println(student1);
        System.out.println(student2);

        try (FileOutputStream fos = new FileOutputStream("test.dat");
            ObjectOutputStream oos = new ObjectOutputStream(fos);) {
            oos.writeObject(student1);
            oos.writeObject(student2);
        } catch (Exception e) {
            e.printStackTrace();
        }

        try (FileInputStream fis = new FileInputStream("test.dat");
             ObjectInputStream ois = new ObjectInputStream(fis);) {
            Student stduent3 = (Student) ois.readObject();
            Student stduent4 = (Student) ois.readObject();
            System.out.println(stduent3);
            System.out.println(stduent4);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

결과

name : 홍길동, grade : 2, code : 4885
name : 아무개, grade : 3, code : 7531
name : 홍길동, grade : 2, code : null
name : 아무개, grade : 3, code : null
블로그 이미지

사용자 yhmane

댓글을 달아 주세요

들어가며


 이전 포스팅에서 자바의 클래스에 대하여 알아보았습니다. 이번 포스팅에선 자바의 클래스에서 한단계 더 나아간 인터페이스와 추상클래스에 대하여 알아보도록 하겠습니다.


추상클래스와 인터페이스

 

인터페이스와 추상 클래스는 객체지향적 개념을 개발에 적용하며 설계를 확장하거나 향후 변경하기 쉬운 구조를 지원하는 요소입니다기본적인 속성과 필요한 메서드의 형태(프로토타입만) 기술하고세부적인 내용은 실제 구현 클래스에서 담당합니다.” 


 자바 실무에서 가장 많이 쓰이는 내용이 아닐까 라고 생각합니다. 공통적인 내용을 추상적으로 만들어 설계하는 곳에서 많이 사용됩니다. 실무에서는 보통 선임개발자가 추상클래스/인터페이스를 설계하여 공통된 로직을 적용하고 후임 개발자들이 내용들을 구현하는 용도로 많이 사용됩니다. 아래는 추상클래스와 인터페이스에 대한 내용을 간략히 표로 정리한 것입니다.


 

 추상 클래스

 인터페이스 

 상속 

 단일상속 

 다중상속 

 구현 

 extends 

 implements 

 추상메서드 

 0개 이상 

 모든 메서드 

 객체생성 

 생성불가 

 생성 불가 


  • 추상클래스

 추상클래스는 일반적인 클래스의 추상화 버전입니다. 추상클래스는 개념이나 사물에서 공통되는 특징이나 속성을 추출하여 설계하는 것입니다.

  • 인터페이스
 인터페이스는 추상클래스와 개념상 거의 동일하지만 아래와 같은 차이가 있습니다.

    • 인터페이스는 일반 메서드를 포함할 수 없으며, 모두 추상 메서드로만 구성합니다.
    • 일반 멤퍼 필드는 없고, public, static, final로 선언한 상수만 있습니다. (java8 부터는 생략 가능)
    • 다중 상속이 가능합니다.

추상클래스 예제

abstract class MyAbstractClass {
int num1, num2;
int result;

void calc() {
result = num1 + num2;
}

abstract int getResult();    // 추상메서드는 선언만
}

class MyClass extends MyAbstractClass  {    // extends로 상속 받음
int getResult() {
return result;
}
}

인터페이스 예제

Interface MyInterface {
public static final num = 1000;
public abstract int getResult();
}

class MyClass implements MyInterface {    // implements로 구현 받음
public int getResult() {
return num + 1000;
}
}



참조

- 자바의 정석

- Just 자바

블로그 이미지

사용자 yhmane

댓글을 달아 주세요

들어가며



 이전 포스팅에선 클래스와 객체, 인스턴스의 차이를 간략히 알아보았습니다. 이번 포스팅에선 클래스의 대해 알아볼 것인데 클래스는 프로그래밍의 설계도라고 생각하시면 됩니다


클래스


 

자바에서 클래스(class)란 객체를 정의하는 틀 또는 설계도라고 합니다. 자바에서는 이러한 설계도인 클래스(class)를 가지고 여러 객체를 생성하여 사용하게 됩니다. 클래스는 객체의 상태를 나타내느 필드(field)와 객체의 행위를 나태는 메서드(method)로 구성됩니다.


- 클래스 작성 규칙

  • 숫자로 시작하지 않는다

  • 첫글자는 대문자로 시작한다

  • 일반적으로 카멜 케이스(둘 이상의 단어가 연결 되었을 때, 두번째 단어의 첫글자는 대문자)를 사용한다

class Student
class CityCode
class ItemOrder


  • 필드

객체지향 개념에서 속성에 해당하는 것으로 멤버 변수라고도 합니다. 구조는 다음과 같습니다.


private String name; 한정자 / 자료형 / 변수명


  • 메서드

객체지향 개념에서 행위에 해당하는 것으로, 클래스를 사용하여 실행하는 동작입니다. 구조는 다음과 같습니다.


public int add(int a, int b) { 한정자 / 리턴값 / 함수명(매개변수 ,,,)

return a + b;            리턴키워드 리턴값

}


접근 한정자 - 외부 클래스에서 해당 클래스에 접근하는 범위를 지정합니다. 범위는 아래 표와 같습니다.

 한정자

클래스 내부 

동일 패키지 

하위 클래스 

그외 여역 

 public

 o

 o 

 o 

 o 

 protected

 o 

 o 

 o 

 x 

 default (기본, 생략가능)

 o 

 o 

 x 

 x 

 private

 o 

 x 

 x 

 x 


인스턴스


 클래스를 이용하여 객체를 생성 하게 되는데, 객체를 new 키워드로 할당하게 되면 인스턴스가 생성되어 집니다. 인스턴스가 생성될 때에는 메모리에 올라간 상태입니다.


 인스턴스 생성 방법


Student student = new Student(); 

클래스   참조변수  생성자

  • 생성자

생성자는 클래스나 인스턴스를 생성할 때 호출하는 특수한 목적의 메서드입니다. 일반적으로 변수를 초기화하거나 필요한 다른 개체를 생성하는 작업을 처리합니다. new 키워드를 사용하여 객체를 생성하는 시점에 호출되며, 클래스는 하나 이상의 생성자를 가질 수 있습니다.


생성자 예제

 public class Student {
private String name;
private int age;

public Student() {        // 기본생성자
name = "윤호";
age = 20;
}

public Student(String name, int age) { // 파라미터가 있는 생성자
this.name = name;
this.age = age;
}
}


public class Main {

public static void main(String[] args) {

Student student = new Student();


System.out.println("학생의 이름 : " + student.getName()); // 윤호

System.out.println("학생의 나이 : " + student.getAge());    // 20


Student a = new Student("철수", 30);

System.out.println("학생의 이름 : " + a.getName());          // 철수

System.out.println("학생의 나이 : " + a.getAge());             // 30

}

}


메서드 오버로딩, 오버라이딩


 마지막으로 자바의 객체지향 특성중 다형성과 관련된 기법인 메서드 오버로딩과 오버라이딩에 대하여 알아보겠습니다.

  • 메서드 오버로딩

 메서드 이름을 동일하지만 파라미터가 다른 여러 메서드를 만드는 것을 말합니다.

메서드 오버로딩 특징

- 메서드 이름이 동일합니다.
- 파라미터의 개수 또는 자료형이 다릅니다.
- 파라미터는 같고 리턴 변수의 자료형이 다르면 오버로딩은 성립하지 않습니다.

메서드 오버로딩 예제

public class OverloadingEx {
public int sum(int num1, int num2) {
return num1 + num2;
}

public int sum(int num1, int num2, num3) {
return num1 + num2 + num3;
}

public static void main(String[] args) {
OverloadingEx  ex = new OverloadingEx();
System.out.println(ex.sum(1,2));            // 3
System.out.println(ex.sum(3,4,5));          // 12
}
}
  • 메서드 오버라이딩

 메서드 오버라이딩은 상위 클래스에서 정의한 메서드를 서브 클래스에서 재정의하는 것을 말합니다.

메서드 오버라이딩 특징

- 메서드 이름이 동일합니다.
- 파라미터와 리턴값이 동일합니다.

메서드 오버라이딩 예제

class Person {
public void run() {
System.out.println("뛴다");
}
}


class Student extends Person {
public void run() {
System.out.println("엄청 빨리 뛴다");
}
}

public class Test {
public static void main(String[] args) {
Person person = new Person();
Student student = new Student();

person.run();    // 뛴다
student.runt();    // 엄청 빨리 뛴다
}
}



출처



- 자바의 정석

- just 자바

블로그 이미지

사용자 yhmane

댓글을 달아 주세요