❓
물음표살인마 블로그
  • README
  • ALGORITHM
    • Sieve of Eratosthenes
    • Round Up
    • Binary Search
    • Union Find
    • Sorting Array
    • Lcm, Gcd
  • TechTalk Review
    • Template
  • Books
    • CS Note for Interview
      • Ch1. Design Pattern & Programming paradigm
        • 1.1.1 Singleton Pattern
        • 1.1.2 Factory Pattern
        • 1.1.4 Observer Pattern
        • 1.1.5 Proxty Pattern & Proxy Server
        • 1.1.8 Model-View-Controller Pattern
        • 1.2.1 Declarative and Functional Programming
        • 1.2.2 Object Oriented Programming
      • Ch2. Network
        • 2.2.1 TCP/IP Four-Layer Model
        • 2.2.1-1 TCP 3, 4 way handshake
        • 2.3 Network Devices L4, L7
        • 2.4.1 ARP, RARP
        • 2.4.2 Hop By Hop Communication
        • 2.4.3 IP Addressing Scheme
      • Ch3. Operating System
        • 3.1.1 Roles and Structure of Operating Systems
        • 3.2.1 Memory Hierarchy
        • 3.2.2 Memory Management
        • 3.3.1 Processes and Compilation Process
        • 3.3.3 Memory Structure of a Process
        • 3.3.4 Process Control Block (PCB)
        • 3.3.5 Multiprocessing
        • 3.3.6 Threads and Multithreading
        • 3.3.7 Shared Resources and Critical Sections
        • 3.3.8 Deadlock
        • 3.4 CPU Scheduling Algorithm
      • Ch4. Database
        • 4.1 Database Basic
        • 4.2 Normalization
        • 4.3 Transaction and Integrity
        • 4.4 Types of Databases
        • 4.5 Indexes
        • 4.6 Types of Joins
        • 4.7 Principles of Joins
      • Ch5. Data Structure
    • Learning the Basics of Large-Scale System Design through Virtual Interview Cases
      • 1. Scalability based on user counts(1/2)
      • 1. Scalability based on user counts(2/2)
      • 2.Back-of-the-envelope estimation
      • 3. Strategies for System Design Interviews
      • 4. Rate Limiter
      • 5. Consistent Hashing
      • 6. Key-Value System Design
      • 7. Designing a Unique ID Generator for Distributed Systems
      • 8. Designing a URL Shortener
      • 9. Designing a Web Crawler
      • 10. Notification System Design
      • 11. Designing a News Feed System
      • 12. Chat System Design
      • 13. AutoComplete
      • 14. Design YouTube
      • 15. Design Google Drive
      • Loadbalancer Algorithms
      • Cache tier
      • CDN, Content Delivery Network
      • Stateless Web tier
    • Computer System A programmer's perspective
    • Effective Java
      • Item 1. Consider Static Factory Methods Instead of Constructors
      • Item 2. Consider a Builder When Faced with Many Constructor Parameters
      • Item 3. Ensure Singleton with Private Constructor or Enum Type
      • Item 4. Enforce Noninstantiability with a Private Constructor
      • Item 5. Prefer Dependency Injection to Hardwiring Resources
      • Item 6. Avoid Creating Unnecessary Objects
      • Item 7. Eliminate Obsolete Object References
      • Item 8. Avoid Finalizers and Cleaners
      • Item 9.Prefer try-with-resources to try-finally
      • Item10. Adhering to General Rules When Overriding equals
        • Handling Transitivity Issues
        • Ensuring Consistency
      • Item11. Override hashCode When You Override equals
      • Item12. Always Override toString
        • Always Override toString
      • Item13. Override Clone Judiciously
      • Item14. Consider Implementing Comparable
      • Item15. Minimize the Accessibility of Classes and Members
      • Item16. Accessor Methods Over Public Fields
      • Item17. Minimize Mutability
      • Item18. Composition over inherentance
      • Item19. Design and Document for Inheritance, or Else Prohibit It
      • Item20. Prefer Interfaces to Abstract Classes
      • Item21. Design Interfaces with Implementations in Mind
      • Item22. Use Interfaces Only to define Types
      • Item23. Prefer Class Hierarchies to Tagged Classes
      • Item24. Favor Static Member Classes Over Non-Static
      • Item28. Use Lists Instead of Arrays
      • Item29. Prefer Generic Types
      • Item30. Favor Generic Methods
    • Head First Design Patterns
      • Ch1. Strategy Pattern
      • Ch2. Observer Pattern
        • Ver1. Ch2. Observer Pattern
      • Ch3. Decorator Pattern
        • Ch3. Decorator Pattern
      • Ch4. Factory Pattern
      • Ch5. Singleton Pattern
      • Ch6. Command Pattern
      • Ch7. Adapter and Facade Pattern
      • Ch8. Template Method Pattern
    • Digging Deep into JVM
      • Chapter 2. Java Memory Area & Memory Overflow
      • Chapter 3. Garbage Collector & Memory Allocation Strategy (1/2)
      • Chapter 3. Garbage Collector & Memory Allocation Strategy (2/2)
      • Chapter 5. Optimization Practice
      • Chapter 6. Class file structure
      • Chapter 8. Bytecode Executor Engine (1/2)
  • Interview Practices
    • Restful API Practices
      • Url Shortener API
      • Event Ticket Reservation API
      • Course Management API
      • Search posts by tags API
      • Online Code platform API
      • Simple Task Management API
      • Event Participation API
      • Review System API
      • Car management API
      • Online Library
    • Tech Review
      • if(kakao)
        • Kakao Account Cache Migration / if(kakao)2022
        • Improving the Anomaly Detection System for KakaoTalk Messaging Metrics / if(kakao) 2022
        • Standardizing API Case Handling Without Redeployment / if(kakaoAI)2024
        • JVM warm up / if(kakao)2022
    • Naver Computer Science
      • Process & Thread
      • TCP & UDP
      • Spring & Servlet
      • Filter & Interceptor & AOP
      • Equals() & ==
      • Dependency Injection
      • Object Oriented Programming
  • F-Lab
    • Week1
      • Client & Server
      • HTTP
      • TCP/UDP
      • REST API
      • Questions
        • Object Oriented Programming
        • HTTP
        • Process & Thread
        • Data Structure
    • Week2
      • OSI 7 layer
      • Web vs WAS
    • Week3
      • RDB vs NoSQL
      • RDB Index
      • Cache
      • Redis
      • Messaging Queue
    • Week4
      • Project - Ecommerce
    • Week5
      • ERD - 1
    • Week6
      • Ecommerce - 2
      • Role
      • pw hashing && Salt
      • CreatedAt, ModifiedAt
      • JWT
      • Copy of ERD - 1
    • Week7
      • Vault (HashiCorp Vault)
    • Week 8
      • Api Endpoints
    • Week10
      • Product Create Workflow
  • TOY Project
    • CodeMentor
      • Implementation of Kafka
      • Project Improvement (Architectural Enhancements)
      • Communication between servers in msa
  • JAVA
    • MESI protocol in CAS
    • CAS (Compare and Set)
    • BlockingQueue
    • Producer & Consumer
    • Synchronized && ReentrantLock
    • Memory Visibility
    • Checked vs Unchecked Exception
    • Thread
    • Batch delete instead of Cascade
    • Java Questions
      • Week 1(1/2) - Basic Java
      • Week 1(2/2) - OOP
      • Week 2(1/2) - String, Exception, Generic
      • Week2(2/2) Lambda, Stream, Annotation, Reflection
      • Week3(1/2) Collections
      • Week3(2/2) Threads
      • Week4 Java Concurrency Programming
      • Week5 JVM & GC
    • Java 101
      • JVM Structure
      • Java Compiles and Execution Method
      • Override, Overload
      • Interface vs Abstract Class
      • Primitive vs Object Type
      • Identity and equality
      • String, StringBuilder, StringBuffer
      • Checked Exceptions and Unchecked Exceptions
      • Java 8 methods
      • Try-with-reources
      • Strong Coupling and Loose Coupling
      • Serialization and Deserialization
      • Concurrency Programming in Java
      • Mutable vs Immutable
      • JDK vs JRE
  • SPRING
    • DIP. Dependency Inversion Principal
    • Ioc container, di practice
    • @Transactional
    • Proxy Pattern
    • Strategy Pattern
    • Template Method Pattern
    • using profile name as variable
    • Spring Questions
      • Spring Framework
      • Spring MVC & Web Request
      • AOP (Aspect-Oriented Programming)
      • Spring Boot
      • ORM & Data Access
      • Security
      • ETC
  • DATABASE
    • Enhancing Query Performance & Stability - User list
    • Ensuring Data Consistency, Atomicity and UX Optimization (feat.Firebase)
    • Redis: Remote Dictionary Server
    • Database Questions
      • Week1 DBMS, RDBMS basics
      • Week2 SQL
      • Week3 Index
      • Week4 Anomaly, Functional Dependency, Normalization
      • Week5 DB Transaction, Recovery
    • Normalization
      • 1st Normal Form
      • 2nd Normal Form
      • 3rd Normal Form
  • NETWORK
    • HTTP & TCP head of line blocking
    • HTTP 0.9-3.0
    • Blocking, NonBlocking and Sync, Async
    • Network Questions
      • Week1 Computer Network Basic
      • Week2(1/3) Application Layer Protocol - HTTP
      • Week2(2/3) Application Layer Protocol - HTTPS
      • Week2(3/3) Application Layer Protocol - DNS
      • Week3 Application Layer
      • Week4 Transport Layer - UDP, TCP
      • Week5 Network Layer - IP Protocol
    • Network 101
      • https://www.google.com
      • TCP vs UDP
      • Http vs Https
      • TLS Handshake 1.2
      • HTTP Method
      • CORS & SOP
      • Web Server Software
  • OS
    • Operating System Questions
      • Week1 OS & How Computer Systems Work
      • Week2(1/2) Process
      • Week2(2/2) Thread
      • Week3 CPU Scheduling
      • Week4 Process Synchronize
      • Week5 Virtual Memory
    • Operating System 101
      • Operating system
        • The role of the operating system
        • The composition of the operating system.
      • Process
        • In Linux, are all processes except the initial process child processes?
        • Zombie process, orphan process
        • (Linux) Daemon process
        • Process address space
        • Where are uninitialized variables stored?
        • Determination of the size of the Stack and Heap
        • Access speed of Stack vs Heap
        • Reason for memory space partitioning
        • Process of compiling a process
        • sudo kill -9 $CURRENT_PID
      • Thread
        • Composition of a thread's address space
      • Process vs Thread
        • Creation of processes and threads in Linux
      • Multiprocessing
        • Web Browser
        • Implementation of multiprocessing
        • Application areas of multiprocessing
      • Multithreading
        • Application areas of multithreading
      • Interrupt
        • HW / SW Interrupt
        • Method of handling interrupts
        • Occurrence of two or more interrupts simultaneously
      • Polling
      • Dual Mode
        • Reason for distinguishing between user mode and kernel mode
      • System call
        • Differentiation between system calls
        • Types of system calls
        • Execution process of a system call
      • Process Control Block (PCB)
        • PCB의 구조
        • 쓰레드는 PCB를 갖고 있을까?
        • 프로세스 메모리 구조
      • Context switching
        • Timing of context switching
        • Registers saved during context switching
        • Context switching in processes
        • Context switching in threads
        • Difference between context switching in processes and threads
        • Information of the current process during context switching
      • Interprocess Communication (IPC)
        • Cases where IPC is used
        • Process address space in IPC Shared Memory technique
        • Types of IPC
  • COMPUTER SCIENCE
    • Computer Architecture 101
      • 3 components of a computer
      • RAM vs ROM
      • CPU vs GPU
      • SIMD
      • Two's complement
      • Harvard Architecture vs. von Neumann Architecture
      • The structure of a CPU.
      • Instruction cycle (CPU operation method)
      • Instruction pipelining
      • Bus
      • Memory area
      • Memory hierarchy structure
        • Reason for using memory hierarchy structure
      • Cache memory
      • L1, L2, L3 Cache
      • Locality of reference (cache)
      • Fixed-point vs Floating-point
        • epresentation of infinity and NaN (Not a Number) in floating-point
      • RISC vs CISC
      • Hamming code
      • Compiler
      • Linking
      • Compiler vs Interpreter
      • Mutex vs Semaphore
      • 32bit CPU and 64bit CPU
      • Local vs Static Variable
      • Page
  • Programming Paradigm
    • Declarative vs Imperative
  • JPA, QueryDsl
    • why fetchResults() is deprecated
  • PYTHON
    • Icecream
  • FASTAPI
    • Template Page
  • LINUX
    • Template Page
  • DATA STRUCTURE
    • Counting Sort
    • Array vs Linked List
  • GIT, Github
    • git clone, invalid path error
  • INFRA
    • Template Page
  • AWS
    • Server Log Archive Pipeline
    • Image Processing using Lambda
  • DOCKER
    • Docker and VM
    • Python Executable Environment
    • Docker commands
  • docker-compose
    • Kafka, Multi Broker
  • KUBERNATES
    • !Encountered Errors
      • my-sql restarts
      • kafka producer: disconnected
    • Kubernetes Components
    • Helm
      • Helm commands
    • Pod network
    • Service network
      • deployment.yaml
      • services.yaml
    • Service type
      • Cluster IP
      • NodePort
    • service-name-headless?
    • kube-proxy
  • GraphQL
    • Template Page
  • WEB
    • Template Page
  • Reviews
    • Graphic Intern Review
    • Kakao Brain Pathfinder Review
    • JSCODE 자바 1기 Review
  • 😁Dev Jokes
    • Image
      • Plot twist
      • Priorities
      • SQL join guide
      • Google is generous
      • Genie dislikes cloud
      • buggy bugs
      • last day of unpaid internship
      • what if clients know how to inspect
      • its just game
      • how i wrote my achievement on resume
      • self explanatory
      • chr(sum(range(ord(min(str(not))))))
Powered by GitBook
On this page
  • 1. 오버라이딩의 개념과 동작 원리
  • 1.1 오버라이딩의 기본 정의
  • 1.2 오버라이딩의 규칙
  • 1.3 오버라이딩의 실제 동작: 동적 바인딩(Dynamic Binding)
  • 2. 오버라이딩과 객체지향 원칙
  • 2.1 다형성(Polymorphism)
  • 2.2 OCP(Open-Closed Principle)
  • 2.3 리스코프 치환원칙 LSP(Liskov Substitution Principle)
  • 2.4 ISP(Interface Segregation Principle)
  • 3. 오버라이딩과 관련된 키워드
  • 3.1 @Override 애노테이션
  • 3.2 super 키워드
  • 3.3 추상 클래스(Abstract Class)와 오버라이딩
  • 3.4 인터페이스(Interface)와 오버라이딩
  • 4. 오버라이딩과 디자인 패턴
  • 4.1 템플릿 메서드 패턴(Template Method Pattern)
  • 4.2 전략 패턴(Strategy Pattern)
  • 4.3 팩토리 메서드 패턴(Factory Method Pattern)
  • 결론
  • 5. 오버로딩의 개념과 동작 원리
  • 5.1 오버로딩의 기본 정의
  • 5.2 오버로딩의 규칙
  • 6. 오버로딩과 객체지향 원칙
  • 6.1 정적 다형성(Static Polymorphism)
  • 6.2 SRP(Single Responsibility Principle)
  • 6.3 DRY(Don't Repeat Yourself) 원칙
  • 7. 오버로딩과 관련된 키워드
  • 7.1 컴파일 타임 결정
  • 7.2 메서드 시그니처(Method Signature)
  • 7.3 디폴트 매개변수와 오버로딩
  • 8. 오버로딩과 디자인 패턴
  • 8.1 팩토리 메서드 패턴(Factory Method Pattern)
  • 8.2 빌더 패턴(Builder Pattern)
  • 결론
  1. JAVA
  2. Java 101

Override, Overload

오버라이딩과 오버로딩

1. 오버라이딩의 개념과 동작 원리

**오버라이딩(Overriding)**은 자바의 객체지향 프로그래밍(OOP)에서 매우 중요한 개념으로, 상속 관계에 있는 부모 클래스의 메서드를 자식 클래스에서 재정의하는 기술입니다. 이는 자바 스프링을 포함한 다양한 프레임워크와 애플리케이션에서 코드의 유연성과 재사용성을 극대화하는 데 중요한 역할을 합니다. 오버라이딩은 다형성(Polymorphism), 동적 바인딩(Dynamic Binding), 그리고 OCP(Open-Closed Principle) 등 여러 객체지향 원칙과 밀접하게 연관되어 있습니다.

1.1 오버라이딩의 기본 정의

오버라이딩은 부모 클래스에서 이미 정의된 메서드를 자식 클래스에서 동일한 메서드 시그니처(메서드 이름, 매개변수의 타입 및 순서)를 사용하여 재정의하는 것을 의미합니다. 이를 통해 자식 클래스는 부모 클래스의 메서드를 상속받되, 자식 클래스에 적합한 동작을 구현할 수 있습니다. 이는 객체지향 프로그래밍의 다형성(Polymorphism)을 구현하는 중요한 방법 중 하나입니다.

class Parent class Child extends Parent
class Parent {
    void showMessage() {
        System.out.println("Message from Parent");
    }
}

class Child extends Parent {
    @Override
    void showMessage() {
        System.out.println("Message from Child");
    }
}

위의 예제에서 Child 클래스는 Parent 클래스의 showMessage 메서드를 오버라이딩하여, Child 객체가 이 메서드를 호출할 때 Child 클래스에서 정의한 메서드가 실행됩니다.

1.2 오버라이딩의 규칙

오버라이딩은 몇 가지 중요한 규칙을 따릅니다:

  1. 메서드 시그니처 일치: 자식 클래스의 오버라이딩된 메서드는 부모 클래스의 메서드와 동일한 메서드 이름과 매개변수 목록을 가져야 합니다.

  2. 접근 제어자 제한: 오버라이딩된 메서드는 부모 클래스의 메서드보다 더 제한적인 접근 제어자를 가질 수 없습니다. 예를 들어, 부모 클래스의 메서드가 protected라면, 자식 클래스에서는 protected나 public으로만 오버라이딩할 수 있고, private으로는 오버라이딩할 수 없습니다.

  3. 예외 처리 제한: 자식 클래스의 오버라이딩된 메서드는 부모 클래스 메서드에서 선언된 예외보다 더 상위의 예외를 던질 수 없습니다. 그러나 부모 클래스에서 던지지 않은 새로운 체크 예외를 던질 수는 없습니다.

  4. 반환 타입의 일관성: 오버라이딩된 메서드는 부모 클래스의 메서드와 동일한 반환 타입을 가지거나, 자바 5 이후로는 더 구체적인 반환 타입(공변 반환 타입, Covariant Return Type)을 가질 수 있습니다.

공변 반환 타입, Covariant Return Type

자바에서 메서드를 오버라이딩할 때, 자식 클래스에서 부모 클래스의 메서드를 재정의하면서 반환 타입을 부모 클래스에서 정의한 반환 타입의 하위 타입으로 변경할 수 있는 기능

class Animal {
    Animal getAnimal() {
        return this;
    }
}

class Dog extends Animal {
    @Override
    Dog getAnimal() {  // 반환 타입을 'Animal'에서 'Dog'로 변경
        return this;
    }
}

위 예제에서, Dog 클래스는 Animal 클래스의 getAnimal() 메서드를 오버라이딩하고 있습니다. 부모 클래스인 Animal의 getAnimal() 메서드는 Animal 타입을 반환하지만, Dog 클래스에서는 이를 Dog 타입으로 변경했습니다.

1.3 오버라이딩의 실제 동작: 동적 바인딩(Dynamic Binding)

오버라이딩은 동적 바인딩(Dynamic Binding) 또는 런타임 바인딩(Runtime Binding)과 관련이 깊습니다. 동적 바인딩은 메서드 호출이 런타임에 실제 객체의 타입에 따라 결정되는 방식입니다. 이는 자바에서 다형성을 실현하는 데 필수적인 요소로, 부모 클래스 타입의 참조 변수가 자식 클래스의 객체를 참조할 때 오버라이딩된 메서드가 실행됩니다.

Parent obj = new Child();
obj.showMessage(); // "Message from Child"

위 코드에서 obj는 Parent 타입의 변수이지만, 실제로는 Child 객체를 참조하고 있습니다. 따라서 showMessage 메서드를 호출할 때 Child 클래스의 메서드가 실행됩니다. 이처럼 자바는 런타임에 객체의 실제 타입을 확인하고, 그에 맞는 메서드를 호출하는 동적 바인딩을 사용합니다.


2. 오버라이딩과 객체지향 원칙

2.1 다형성(Polymorphism)

오버라이딩은 다형성을 구현하는 핵심 기법입니다. 다형성은 같은 인터페이스나 부모 클래스를 공유하는 객체들이 동일한 메서드 호출에 대해 서로 다른 방식으로 응답할 수 있게 해줍니다. 자바에서는 오버라이딩된 메서드를 통해 다양한 형태의 동작을 구현할 수 있으며, 이는 코드의 유연성과 확장성을 크게 높여줍니다.

Parent obj1 = new Parent();
Parent obj2 = new Child();

obj1.showMessage(); // "Message from Parent"
obj2.showMessage(); // "Message from Child"

이 예시에서 두 객체는 같은 타입(Parent)이지만, 각각의 메서드 호출에 대해 다른 결과를 반환합니다. 이는 다형성을 활용한 코드로, 코드의 재사용성과 유지보수성을 높입니다.

2.2 OCP(Open-Closed Principle)

OCP는 "확장에는 열려 있고, 수정에는 닫혀 있어야 한다"는 객체지향 설계 원칙으로, 기존 코드를 수정하지 않고 새로운 기능을 추가할 수 있어야 한다는 의미입니다. 오버라이딩은 OCP를 실현하는 데 중요한 역할을 합니다. 자식 클래스에서 부모 클래스의 메서드를 오버라이딩함으로써 기존 클래스 코드를 수정하지 않고도 새로운 기능을 구현할 수 있습니다.

class Shape class Circle extends Shape class Square extends Shape
class Shape {
    void draw() {
        System.out.println("Drawing a shape");
    }
}

class Circle extends Shape {
    @Override
    void draw() {
        System.out.println("Drawing a circle");
    }
}

class Square extends Shape {
    @Override
    void draw() {
        System.out.println("Drawing a square");
    }
}

위 코드에서 Shape 클래스는 수정하지 않고, Circle과 Square 클래스에서 새로운 동작을 구현했습니다. 이는 OCP를 따르는 설계 방식입니다.

2.3 리스코프 치환원칙 LSP(Liskov Substitution Principle)

LSP는 "서브 타입은 언제나 자신의 기반 타입으로 대체할 수 있어야 한다"는 원칙입니다. 오버라이딩을 통해 자식 클래스는 부모 클래스의 인터페이스를 일관되게 유지하며, 부모 클래스의 역할을 대체할 수 있습니다. 이는 코드에서 자식 클래스가 부모 클래스로 대체되어도 프로그램의 정확성과 기능이 유지될 수 있음을 보장합니다.

잘못된 사례 class Rectangle class Square extends Rectangle
class Rectangle {
    int width, height;

    void setWidth(int width) {
        this.width = width;
    }

    void setHeight(int height) {
        this.height = height;
    }

    int area() {
        return width * height;
    }
}

class Square extends Rectangle {
    @Override
    void setWidth(int width) {
        this.width = width;
        this.height = width;
    }

    @Override
    void setHeight(int height) {
        this.width = height;
        this.height = height;
    }
}

Square 클래스는 Rectangle 클래스를 상속받고 있지만, LSP(Liskov Substitution Principle)를 위반하고 있습니다. LSP는 자식 클래스가 부모 클래스의 기능을 유지하면서도 확장할 수 있어야 한다는 원칙입니다. 하지만 이 예제에서는 Square가 Rectangle의 메서드를 오버라이딩하여 Rectangle의 기본 기능을 왜곡하고 있습니다. 구체적으로는, Square가 사각형의 특성(너비와 높이가 서로 다를 수 있음)을 무시하고, 이를 고정시킴으로써 Rectangle의 본래 역할을 수행할 수 없게 만듭니다.

상속 대신 별도의 클래스 구조 사용 interface Shape class Rectangle implements Shape class Square implements Shape
interface Shape {
    int area();
}

class Rectangle implements Shape {
    protected int width, height;

    void setWidth(int width) {
        this.width = width;
    }

    void setHeight(int height) {
        this.height = height;
    }

    @Override
    public int area() {
        return width * height;
    }
}

class Square implements Shape {
    private int side;

    void setSide(int side) {
        this.side = side;
    }

    @Override
    public int area() {
        return side * side;
    }
}

이렇게 하면 Rectangle과 Square는 각각 자신의 특성에 맞게 설계되고, 공통 기능인 area() 메서드는 Shape 인터페이스를 통해 구현됩니다. 이 접근법은 LSP를 준수하며, 클래스들이 각각의 역할을 명확히 할 수 있게 합니다.

2.4 ISP(Interface Segregation Principle)

ISP는 "클라이언트는 자신이 사용하지 않는 인터페이스에 의존하지 않아야 한다"는 원칙입니다. 오버라이딩을 통해 자식 클래스는 필요 없는 부모 클래스의 메서드를 오버라이딩하지 않거나, 특정 기능만 구현하도록 설계할 수 있습니다. 이는 인터페이스의 분리를 통해 코드의 응집도를 높이고, 불필요한 의존성을 줄일 수 있습니다.

잘못된 사례 interface Worker class Developer implements Worker lass Manager implements Worker
interface Worker {
    void work();
    void eat();
}

class Developer implements Worker {
    @Override
    public void work() {
        System.out.println("Writing code");
    }

    @Override
    public void eat() {
        // Developer가 일과 관련된 코드만 필요하지만, eat 메서드를 구현해야 합니다.
        System.out.println("Eating lunch");
    }
}

class Manager implements Worker {
    @Override
    public void work() {
        System.out.println("Managing project");
    }

    @Override
    public void eat() {
        System.out.println("Eating lunch");
    }
}

이 예제에서 Worker 인터페이스는 work()와 eat() 메서드를 모두 포함하고 있습니다. Developer와 Manager 클래스는 실제로는 work() 메서드만 필요하지만, eat() 메서드도 구현해야 합니다. 이는 불필요한 의존성을 증가시키고, 인터페이스의 응집도를 낮춥니다.

작은 인터페이스로 분리 interface Workable interface Eatable class Developer implements Workable class Manager implements Workable, Eatable

ISP를 적용하여, 클라이언트가 자신에게 필요한 기능만 구현하도록 인터페이스를 분리할 수 있습니다.

interface Workable {
    void work();
}

interface Eatable {
    void eat();
}

class Developer implements Workable {
    @Override
    public void work() {
        System.out.println("Writing code");
    }
}

class Manager implements Workable, Eatable {
    @Override
    public void work() {
        System.out.println("Managing project");
    }

    @Override
    public void eat() {
        System.out.println("Eating lunch");
    }
}

이 예제에서 Workable과 Eatable 인터페이스를 분리하여, Developer 클래스는 자신에게 필요한 work() 메서드만 구현하고, Manager 클래스는 work()와 eat() 메서드를 모두 구현하도록 설계했습니다. 이로써 불필요한 메서드를 구현하지 않아도 되며, 인터페이스가 클라이언트의 요구에 맞게 분리되어 코드의 응집도가 높아지고, 불필요한 의존성이 제거되었습니다.


3. 오버라이딩과 관련된 키워드

3.1 @Override 애노테이션

@Override 애노테이션은 자바에서 메서드가 부모 클래스의 메서드를 오버라이딩하고 있음을 명시적으로 선언하는 데 사용됩니다. 이 애노테이션을 사용하면 컴파일러가 메서드 시그니처의 일치를 검증하여, 실수로 잘못된 시그니처를 작성하는 것을 방지할 수 있습니다. 이는 코드의 가독성과 유지보수성을 높이는 데 중요한 역할을 합니다.

메서드 시그니처란?

메서드 시그니처는 메서드의 이름과 매개변수의 타입 및 개수를 합한 것

class Parent {
    void display(String message) {
        System.out.println(message);
    }
}

class Child extends Parent {
    @Override
    void display(String message) {
        System.out.println("Child: " + message);
    }
}

여기서 display(String message)는 display라는 메서드 이름과 String 타입의 매개변수를 가지며, Parent와 Child 클래스 모두 동일한 메서드 시그니처를 가지고 있습니다.

3.2 super 키워드

super 키워드는 자식 클래스에서 부모 클래스의 메서드나 생성자를 호출할 때 사용됩니다. 오버라이딩된 메서드 내에서 부모 클래스의 원래 메서드를 호출해야 하는 경우, super를 사용하여 부모 클래스의 메서드에 접근할 수 있습니다.

class Parent class Child extends Parent
class Parent {
    void showMessage() {
        System.out.println("Message from Parent");
    }
}

class Child extends Parent {
    @Override
    void showMessage() {
        super.showMessage(); // 부모 클래스의 메서드 호출
        System.out.println("Message from Child");
        // 실행 결과:
        // Message from Parent
        // Message from Child
    }
}

위의 예제에서 Child 클래스는 부모 클래스의 showMessage 메서드를 호출한 후, 자신의 메서드 작업을 추가로 수행합니다.

3.3 추상 클래스(Abstract Class)와 오버라이딩

추상 클래스는 하나 이상의 추상 메서드를 포함할 수 있는 클래스입니다. 추상 메서드는 구현이 없는 메서드로, 자식 클래스에서 반드시 오버라이딩하여 구현해야 합니다. 이를 통해 부모 클래스에서 메서드의 기본 구조만 정의하고, 구체적인 구현은 자식 클래스에서 하도록 강제할 수 있습니다.

abstract class Animal class Dog extends Animal class Cat extends Animal
abstract class Animal {
    abstract void sound();
}

class Dog extends Animal {
    @Override
    void sound() {
        System.out.println("Bark");
    }
}

class Cat extends Animal {
    @Override
    void sound() {
        System.out.println("Meow");
    }
}

위의 예제에서 Animal 클래스는 sound라는 추상 메서드를 가지며, 각 자식 클래스는 이를 오버라이딩하여 자신만의 소리를 구현합니다.

3.4 인터페이스(Interface)와 오버라이딩

인터페이스는 클래스가 구현해야 할 메서드의 시그니처를 정의하는 일종의 계약(contract)입니다. 클래스가 인터페이스를 구현할 때, 인터페이스에 정의된 모든 메서드를 오버라이딩해야 합니다. 이는 다형성을 실현하는 또 다른 방법으로, 자바 스프링에서 의존성 주입(Dependency Injection)이나 스프링 빈(Spring Bean) 등 다양한 기능에 활용됩니다.

interface Printer class ConsolePrinter implements Printer class FilePrinter implements Printer
interface Printer {
    void print();
}

class ConsolePrinter implements Printer {
    @Override
    public void print() {
        System.out.println("Printing to console");
    }
}

class FilePrinter implements Printer {
    @Override
    public void print() {
        System.out.println("Printing to file");
    }
}

이 예제에서 ConsolePrinter와 FilePrinter 클래스는 각각 Printer 인터페이스의 print 메서드를 오버라이딩하여 자신만의 출력을 구현합니다.

abstract class와 interface의 기본 차이

특징

abstract class

interface

목적

공통된 동작과 상태를 정의하며, 일부 구현을 제공

행동의 계약을 정의하며, 구현은 전혀 포함하지 않음

구현

일반 메서드와 추상 메서드 모두 포함 가능

디폴트 메서드와 정적 메서드를 제외하고 모든 메서드가 추상 메서드

상속

단일 상속만 가능

다중 구현 가능

필드

인스턴스 변수와 상수 선언 가능

상수만 선언 가능 (자바 8부터 디폴트 메서드와 정적 메서드 포함 가능)

생성자

생성자 선언 가능

생성자 선언 불가

사용 시기

기본 동작이나 상태를 공유하는 클래스들이 있을 때 사용

클래스들이 공통적으로 가져야 할 행동을 정의할 때 사용


4. 오버라이딩과 디자인 패턴

4.1 템플릿 메서드 패턴(Template Method Pattern)

템플릿 메서드 패턴은 알고리즘의 구조를 부모 클래스에서 정의하고, 구체적인 구현을 자식 클래스에서 오버라이딩하여 수행하는 패턴입니다. 이 패턴은 공통적인 알고리즘 구조를 유지하면서도, 세부적인 동작을 자식 클래스에서 유연하게 변경할 수 있게 해줍니다.

abstract class Game class Football extends Game class Cricket extends Game
abstract class Game {
    final void play() {
        initialize();
        startPlay();
        endPlay();
    }

    abstract void initialize();
    abstract void startPlay();
    abstract void endPlay();
}

class Football extends Game {
    @Override
    void initialize() {
        System.out.println("Football Game Initialized.");
    }

    @Override
    void startPlay() {
        System.out.println("Football Game Started.");
    }

    @Override
    void endPlay() {
        System.out.println("Football Game Finished.");
    }
}

class Cricket extends Game {
    @Override
    void initialize() {
        System.out.println("Cricket Game Initialized.");
    }

    @Override
    void startPlay() {
        System.out.println("Cricket Game Started.");
    }

    @Override
    void endPlay() {
        System.out.println("Cricket Game Finished.");
    }
}

위의 예제에서 Game 클래스는 템플릿 메서드를 정의하고, 세부적인 단계는 Football과 Cricket 클래스에서 오버라이딩하여 구현합니다.

4.2 전략 패턴(Strategy Pattern)

전략 패턴은 다양한 알고리즘을 인터페이스로 캡슐화하여 상호 교체 가능하게 만드는 패턴입니다. 오버라이딩을 통해 각 전략 클래스는 동일한 인터페이스를 구현하면서도, 자신만의 독립적인 동작을 정의할 수 있습니다. 이 패턴은 런타임에 전략을 변경하거나 확장할 수 있는 유연성을 제공합니다.

interface PaymentStrategy class CreditCardPayment implements PaymentStrategy class PaypalPayment implements PaymentStrategy
interface PaymentStrategy {
    void pay(int amount);
}

class CreditCardPayment implements PaymentStrategy {
    @Override
    public void pay(int amount) {
        System.out.println("Paid " + amount + " using Credit Card");
    }
}

class PaypalPayment implements PaymentStrategy {
    @Override
    public void pay(int amount) {
        System.out.println("Paid " + amount + " using PayPal");
    }
}

위의 예제에서 PaymentStrategy 인터페이스를 구현한 CreditCardPayment와 PaypalPayment 클래스는 각각의 pay 메서드를 오버라이딩하여 다른 방식의 결제를 구현합니다.

4.3 팩토리 메서드 패턴(Factory Method Pattern)

팩토리 메서드 패턴은 객체 생성의 책임을 서브클래스에 위임하는 디자인 패턴입니다. 이 패턴에서는 부모 클래스가 객체 생성에 필요한 인터페이스만 제공하고, 실제 객체 생성은 자식 클래스에서 오버라이딩하여 수행합니다.

abstract class Creator class ConcreteCreator extends Creator
abstract class Creator {
    abstract Product createProduct();

    void doSomething() {
        Product product = createProduct();
        product.use();
    }
}

class ConcreteCreator extends Creator {
    @Override
    Product createProduct() {
        return new ConcreteProduct();
    }
}

이 예제에서 ConcreteCreator 클래스는 createProduct 메서드를 오버라이딩하여 ConcreteProduct 객체를 생성합니다. 팩토리 메서드 패턴은 객체 생성 로직을 캡슐화하고, 서브클래스를 통해 다양한 객체 생성 방식을 제공할 수 있도록 합니다.


결론

오버라이딩은 자바에서 객체지향 프로그래밍의 핵심 원칙을 구현하는 데 필수적인 기법으로, 특히 자바 스프링과 같은 프레임워크에서 다형성, 동적 바인딩, OCP, LSP 등의 원칙과 결합하여 코드의 유연성과 확장성을 높이는 데 중요한 역할을 합니다. 오버라이딩을 제대로 이해하고 활용하는 것은 견고하고 유지보수하기 쉬운 소프트웨어를 개발하는 데 필수적입니다. 이를 통해 코드는 재사용 가능하고, 변경에 유연하며, 다양한 디자인 패턴을 통해 구조화할 수 있습니다.


5. 오버로딩의 개념과 동작 원리

**오버로딩(Overloading)**은 자바의 객체지향 프로그래밍(OOP)에서 하나의 클래스 내에서 동일한 이름의 메서드를 여러 개 정의하되, 각 메서드가 서로 다른 매개변수 리스트를 가지도록 하는 기법입니다. 이는 메서드의 이름을 재사용하면서도 다양한 입력을 처리할 수 있게 해주며, 코드의 가독성을 높이고 유연성을 제공합니다. 오버로딩은 컴파일 타임에 결정되며, 자바에서 정적 다형성(Static Polymorphism)을 구현하는 방법 중 하나입니다.

5.1 오버로딩의 기본 정의

오버로딩은 동일한 메서드 이름을 사용하지만, 서로 다른 매개변수의 타입, 개수, 또는 순서를 사용하여 여러 개의 메서드를 정의하는 것입니다. 이렇게 하면 같은 이름의 메서드가 다양한 입력에 대해 다르게 동작하도록 할 수 있습니다. 이는 코드의 가독성을 높이고, 개발자가 메서드 이름을 일관되게 사용할 수 있게 해줍니다.

class MathOperations
class MathOperations {
    int add(int a, int b) {
        return a + b;
    }

    double add(double a, double b) {
        return a + b;
    }

    int add(int a, int b, int c) {
        return a + b + c;
    }
}

위의 예제에서 MathOperations 클래스는 add 메서드를 세 번 오버로딩하고 있습니다. 각 add 메서드는 매개변수의 타입과 개수가 다르며, 이를 통해 다양한 형태의 덧셈을 지원합니다.

5.2 오버로딩의 규칙

오버로딩을 사용할 때는 몇 가지 중요한 규칙을 따라야 합니다:

  1. 메서드 이름 동일: 오버로딩된 메서드들은 모두 동일한 메서드 이름을 가져야 합니다.

  2. 매개변수의 차이: 오버로딩된 메서드들은 매개변수의 타입, 개수, 또는 순서가 달라야 합니다. 매개변수의 이름만 다른 경우 오버로딩이 성립되지 않습니다.

  3. 반환 타입: 반환 타입이 다른 것은 오버로딩에 영향을 미치지 않습니다. 오버로딩은 오로지 매개변수 리스트의 차이에 따라 결정됩니다.

class Display
class Display {
    void show(int a) {
        System.out.println("Integer: " + a);
    }

    void show(String a) {
        System.out.println("String: " + a);
    }

    void show(int a, String b) {
        System.out.println("Integer: " + a + ", String: " + b);
    }
}

이 예제에서 show 메서드는 매개변수의 타입과 개수에 따라 여러 번 오버로딩되어 있습니다.


6. 오버로딩과 객체지향 원칙

6.1 정적 다형성(Static Polymorphism)

오버로딩은 자바에서 **정적 다형성(Static Polymorphism)**을 구현하는 방법입니다. 컴파일 타임에 호출할 메서드가 결정되기 때문에, 이를 컴파일 타임 다형성이라고도 합니다. 이는 런타임에 메서드가 결정되는 오버라이딩과 대비됩니다.

class MathOperations
class MathOperations {
    int multiply(int a, int b) {
        return a * b;
    }

    double multiply(double a, double b) {
        return a * b;
    }
}

public class Main {
    public static void main(String[] args) {
        MathOperations math = new MathOperations();
        System.out.println(math.multiply(2, 3)); // multiply(int, int) 호출
        System.out.println(math.multiply(2.5, 3.5)); // multiply(double, double) 호출
    }
}

위의 예제에서 컴파일러는 각 multiply 메서드 호출 시점에 메서드의 시그니처를 분석하여 적절한 메서드를 호출합니다.

6.2 SRP(Single Responsibility Principle)

오버로딩은 SRP와도 관련이 있습니다. SRP는 "클래스는 하나의 책임만 가져야 한다"는 원칙으로, 오버로딩된 메서드들은 같은 이름을 가지지만, 입력 매개변수의 타입이나 개수에 따라 서로 다른 기능을 수행할 수 있습니다. 이를 통해 클래스가 하나의 책임을 중심으로 다양한 상황에 대처할 수 있도록 설계할 수 있습니다.

class Logger
class Logger {
    void log(String message) {
        System.out.println("Log: " + message);
    }

    void log(int errorCode) {
        System.out.println("Error Code: " + errorCode);
    }

    void log(String message, int errorCode) {
        System.out.println("Log: " + message + " (Error Code: " + errorCode)");
    }
}

이 예제에서 Logger 클래스는 다양한 타입의 로그 메시지를 처리할 수 있도록 오버로딩된 log 메서드를 제공합니다. 이는 로그를 기록하는 하나의 책임을 중심으로, 다양한 입력을 처리하는 방법을 제시합니다.

6.3 DRY(Don't Repeat Yourself) 원칙

오버로딩은 DRY 원칙을 준수하는 데 도움이 됩니다. DRY 원칙은 "중복을 피하라"는 의미로, 코드의 중복을 줄임으로써 유지보수성을 높이는 원칙입니다. 오버로딩을 통해 동일한 목적을 가진 메서드들을 한 곳에 모아둠으로써 코드의 중복을 피하고, 일관된 메서드 이름을 유지할 수 있습니다.

DRY(Don't Repeat Yourself) 원칙

DRY 원칙의 중요성

  1. 유지보수성 향상: 코드가 중복되면, 동일한 로직에 변경이 필요할 때 모든 중복된 부분을 수정해야 합니다. 이는 실수를 초래할 수 있으며, 유지보수를 어렵게 만듭니다. DRY 원칙을 따르면, 수정이 필요한 부분이 한 곳에만 존재하므로 유지보수가 쉬워집니다.

  2. 가독성 향상: 코드가 중복되면 코드베이스가 불필요하게 길어져, 가독성이 떨어집니다. DRY 원칙을 따르면, 코드는 더 간결해지고, 이해하기 쉬워집니다.

  3. 재사용성 증가: 중복을 피하고, 공통된 기능을 하나의 모듈로 분리하면, 이 기능을 여러 곳에서 재사용할 수 있습니다. 이는 코드의 재사용성을 높여 개발 효율성을 향상시킵니다.

DRY 원칙을 구현하는 방법

  1. 함수와 메서드의 활용: 반복되는 코드를 함수나 메서드로 추출하여, 재사용할 수 있습니다.

    // DRY 원칙을 위반한 코드
    System.out.println("User ID: 123");
    System.out.println("User Name: Alice");
    System.out.println("User Email: alice@example.com");
    
    System.out.println("User ID: 456");
    System.out.println("User Name: Bob");
    System.out.println("User Email: bob@example.com");
    
    // DRY 원칙을 준수한 코드
    void printUserInfo(int id, String name, String email) {
        System.out.println("User ID: " + id);
        System.out.println("User Name: " + name);
        System.out.println("User Email: " + email);
    }
    
    // 함수 호출
    printUserInfo(123, "Alice", "alice@example.com");
    printUserInfo(456, "Bob", "bob@example.com");
  2. 상속과 인터페이스 사용: 공통된 기능을 부모 클래스나 인터페이스로 추출하여, 이를 상속하거나 구현함으로써 중복을 피할 수 있습니다.

    // DRY 원칙을 위반한 코드
    class Dog {
        void makeSound() {
            System.out.println("Bark");
        }
    }
    
    class Cat {
        void makeSound() {
            System.out.println("Meow");
        }
    }
    
    // DRY 원칙을 준수한 코드
    abstract class Animal {
        abstract void makeSound();
    }
    
    class Dog extends Animal {
        @Override
        void makeSound() {
            System.out.println("Bark");
        }
    }
    
    class Cat extends Animal {
        @Override
        void makeSound() {
            System.out.println("Meow");
        }
    }
  3. 상수와 변수의 활용: 반복되는 값이나 표현식을 상수나 변수로 추출하여 중복을 피할 수 있습니다.

    // DRY 원칙을 위반한 코드
    double radius1 = 7;
    double area1 = 3.14159 * radius1 * radius1;
    
    double radius2 = 14;
    double area2 = 3.14159 * radius2 * radius2;
    
    // DRY 원칙을 준수한 코드
    final double PI = 3.14159;
    
    double calculateArea(double radius) {
        return PI * radius * radius;
    }
    
    double area1 = calculateArea(7);
    double area2 = calculateArea(14);

7. 오버로딩과 관련된 키워드

7.1 컴파일 타임 결정

오버로딩된 메서드는 컴파일 타임에 어떤 메서드가 호출될지가 결정됩니다. 자바 컴파일러는 메서드 호출 시점에서 전달된 인수의 타입과 개수를 분석하여 적절한 메서드를 선택합니다. 이로 인해 오버로딩된 메서드는 성능 면에서 효율적이며, 컴파일러가 이를 최적화할 수 있습니다.

class Calculator
class Calculator {
    int add(int a, int b) {
        return a + b;
    }

    double add(double a, double b) {
        return a + b;
    }
}

public class Main {
    public static void main(String[] args) {
        Calculator calc = new Calculator();
        System.out.println(calc.add(2, 3)); // 컴파일 타임에 add(int, int) 선택
        System.out.println(calc.add(2.5, 3.5)); // 컴파일 타임에 add(double, double) 선택
    }
}

이 예제에서 각 add 메서드는 컴파일 타임에 적절한 메서드가 선택되어 호출됩니다.

7.2 메서드 시그니처(Method Signature)

메서드 시그니처는 메서드 이름과 매개변수 리스트(타입과 순서)를 합한 것을 의미합니다. 오버로딩은 동일한 메서드 이름을 가지면서도 서로 다른 시그니처를 가진 메서드들을 정의할 수 있게 해줍니다.

class Printer
class Printer {
    void print(String message) {
        System.out.println("String: " + message);
    }

    void print(int number) {
        System.out.println("Integer: " + number);
    }

    void print(String message, int number) {
        System.out.println("String: " + message + ", Integer: " + number);
    }
}

여기서 print 메서드는 서로 다른 시그니처를 가지므로 오버로딩된 메서드로 취급됩니다.

7.3 디폴트 매개변수와 오버로딩

자바에서는 C++ 같은 언어와 달리, 디폴트 매개변수를 직접적으로 지원하지 않습니다. 그러나 오버로딩을 통해 비슷한 효과를 구현할 수 있습니다. 여러 오버로딩된 메서드를 정의하여 매개변수의 개수를 다양하게 받아들이도록 하여, 디폴트 매개변수를 사용하는 것처럼 동작할 수 있습니다.

class Greeter
class Greeter {
    void greet(String name) {
        System.out.println("Hello, " + name);
    }

    void greet() {
        greet("Guest"); // 디폴트 이름으로 오버로딩 사용
    }
}

이 예제에서 greet() 메서드는 greet(String name) 메서드를 호출하면서 기본 값을 전달하여 디폴트 매개변수처럼 작동합니다.


8. 오버로딩과 디자인 패턴

8.1 팩토리 메서드 패턴(Factory Method Pattern)

팩토리 메서드 패턴은 객체 생성의 책임을 서브클래스에 위임하는 디자인 패턴입니다. 오버로딩된 메서드를 통해 팩토리 메서드 패턴을 구현할 수 있으며, 다양한 매개변수에 따라 서로 다른 객체를 생성할 수 있습니다.

class ShapeFactory
class ShapeFactory {
    Shape createShape(String type) {
        if (type.equals("circle")) {
            return new Circle();
        } else if (type.equals("rectangle")) {
            return new Rectangle();
        }
        return null;
    }

    Shape createShape(int sides) {
        if (sides == 3) {
            return new Triangle();
        } else if (sides == 4) {
            return new Square();
        }
        return null;
    }
}

이 예제에서 ShapeFactory 클래스는 createShape 메서드를 오버로딩하여, 입력된 매개변수에 따라 서로 다른 Shape 객체를 생성합니다.

8.2 빌더 패턴(Builder Pattern)

빌더 패턴은 복잡한 객체를 단계별로 생성할 수 있도록 하는 디자인 패턴입니다. 빌더 패턴에서 오버로딩을 사용하면 객체 생성 시 다양한 조합의 매개변수를 허용하여 객체를 유연하게 구성할 수 있습니다.

class Computer
class Computer {
    private String CPU;
    private int RAM;
    private int storage;

    // 필수 매개변수만으로 생성
    Computer(String CPU) {
        this.CPU = CPU;
    }

    // 필수 매개변수 + 선택적 매개변수로 생성
    Computer(String CPU, int RAM) {
        this(CPU);
        this.RAM = RAM;
    }

    // 모든 매개변수로 생성
    Computer(String CPU, int RAM, int storage) {
        this(CPU, RAM);
        this.storage = storage;
    }
}

이 예제에서 Computer 클래스는 여러 개의 생성자를 오버로딩하여, 매개변수에 따라 객체를 다양한 방식으로 생성할 수 있습니다.


결론

오버로딩은 자바에서 메서드 이름의 일관성을 유지하면서도 다양한 입력을 처리할 수 있도록 하는 강력한 기법입니다. 이를 통해 코드의 가독성과 유연성이 향상되며, 정적 다형성을 구현할 수 있습니다. 오버로딩을 적절히 활용하면 SRP와 DRY 같은 객체지향 원칙을 준수하면서도 다양한 설계 패턴을 효과적으로 구현할 수 있습니다. 이러한 기법을 이해하고 적용함으로써 자바 코드를 더욱 견고하고 유지보수하기 쉽게 만들 수 있습니다.

PreviousJava Compiles and Execution MethodNextInterface vs Abstract Class

Last updated 9 months ago