Java 자바의 기초 (다형성, 추상클래스, final, 인터페이스)

개발일지

이번 글에서는 객체 지향 프로그래밍에서 중요한 다형성을 포함한 내용을 정리해보려 합니다. 기본 개념을 학습하는 것도 중요하지만, 실제로 코드를 작성하면서 익숙해지는 것도 중요한 것 같습니다. 다형성을 활용한 프로그래밍의 유용한 점들을 알게 되는 시간이였습니다.

그럼 다형성, 추상 클래스, final, 인터페이스에 대해 중요한 내용을 중심으로 간략하게 정리해보겠습니다.

다형성

다형성은 객체 지향 프로그래밍에서의 중요한 특징 중 하나로, 다양한 형태로 나타날 수 있는 능력을 의미합니다. 다형성은 주로 메소드 오버로딩(Overloading)과 메소드 오버라이딩(Overriding)을 통해 나타납니다.

한 가지 타입으로 여러 객체 다루기

다형성을 통해 부모 클래스 타입의 변수에 여러 종류의 자식 클래스 객체를 할당할 수 있습니다. 이로써 여러 객체들을 일관된 방식으로 다룰 수 있습니다.

Java
부모클래스 변수 = new 자식클래스();
Java

업캐스팅과 다운캐스팅

  • 업캐스팅(Upcasting): 자식 클래스의 인스턴스를 부모 클래스 타입으로 변환합니다.
  • 다운캐스팅(Downcasting): 부모 클래스 타입의 변수를 다시 자식 클래스 타입으로 변환합니다. 명시적인 형변환이 필요합니다.
Java
// 업캐스팅
동물 동물객체 = new 개();

// 다운캐스팅
개 개객체 = (개) 동물객체;
Java

메소드 오버로딩과 오버라이딩

  • 메소드 오버로딩: 동일한 메소드 이름을 가지면서 매개변수의 타입, 개수, 순서 등이 다르게 정의된 여러 메소드를 의미합니다.
  • 메소드 오버라이딩: 부모 클래스의 메소드를 자식 클래스에서 재정의하는 것을 의미합니다.
Java
// 메소드 오버로딩
void printMessage(String message) {}
void printMessage(int number) {}

// 메소드 오버라이딩
class 부모클래스 {
    void 소리내기() {
        System.out.println("동물이 소리를 냅니다.");
    }
}

class  extends 부모클래스 {
    @Override
    void 소리내기() {
        System.out.println("개가 멍멍 소리를 냅니다.");
    }
}
Java

다형성을 통한 유연성 제공

다형성을 적절히 사용하면 코드의 유연성이 증가하며, 변경이나 확장에 대한 대응이 용이해집니다.

Java
void 동물소리내기(동물 animal) {
    animal.소리내기();
}

// 다양한 동물 객체를 처리할 수 있음 (다형성)
동물소리내기(new 동물());
동물소리내기(new 개());
Java

다형성은 객체 지향 프로그래밍의 강력한 특징 중 하나로, 코드의 재사용성을 높이고 유연성을 제공하여 프로그램의 유지보수와 확장성을 향상시킵니다.

추상클래스

추상 클래스는 다른 클래스들에게 공통된 특징을 제공하고 메소드의 일부를 구현하는데 사용되며, 다음과 같은 방식으로 사용됩니다.

추상 클래스 선언

추상 클래스는 abstract 키워드를 사용하여 선언됩니다. 추상 메소드를 가질 수 있으며, 일반 메소드도 포함할 수 있습니다.

Java
abstract class Shape {
    abstract void draw(); // 추상 메소드
    void printInfo() {
        System.out.println("도형 정보 출력");
    }
}
Java

추상 클래스 상속

추상 클래스를 상속받는 클래스를 정의합니다. 하위 클래스에서는 추상 메소드를 반드시 구현해야 합니다. 일반 메소드는 구현을 선택적으로 재정의할 수 있습니다.

Java
class Circle extends Shape {
    @Override
    void draw() {
        System.out.println("원을 그립니다.");
    }
}
Java

추상 클래스 활용

하위 클래스를 사용하여 객체를 생성하고, 추상 클래스에 정의된 메소드를 호출할 수 있습니다. 이때 하위 클래스에서 구현한 메소드가 실행됩니다.

Java
public class Main {
    public static void main(String[] args) {
        Shape circle = new Circle();
        circle.draw();      // 하위 클래스에서 구현한 draw() 호출
        circle.printInfo(); // 추상 클래스에서 구현한 printInfo() 호출
    }
}
Java
  • 추상클래스는 객체를 직접 생성할 수 없습니다.
  • 추상클래스의 추상메소드를 상속 받은 하위 클래스는 반드시 메소드를 구현해야 객체로 생성할 수 있습니다.

추상 클래스를 사용하면 코드의 일관성을 유지하고, 여러 클래스에서 공통적으로 사용되는 메소드를 추상 클래스에서 구현하여 중복을 최소화할 수 있습니다. 추상 클래스를 통해 상속을 통한 다형성을 활용하여 유연하고 확장 가능한 코드를 작성할 수 있습니다.

final

자바에서 final은 변수, 메소드, 클래스에 적용될 수 있는 키워드로, 해당 선언이 최종적이거나 변경될 수 없음을 나타냅니다. final 키워드는 프로그램의 안정성, 효율성, 보안성 등을 강화하는 데 사용됩니다.

final 변수 (상수)

  • final 변수는 한 번 값을 할당하면 다시 할당할 수 없습니다.
  • 주로 상수 값, 즉 변경되지 않아야 하는 값을 나타내는 데 사용됩니다.
  • 관례적으로 상수 이름은 대문자로 작성되며, 단어 사이는 언더스코어(_)로 구분됩니다.
Java
public class Example {
    final int CONSTANT_VALUE = 10; // final 변수 선언과 초기화

    public void setConstantValue() {
        // CONSTANT_VALUE = 20; // 오류! final 변수는 재할당할 수 없음
    }
}
Java

final 메소드

  • final로 선언된 메소드는 하위 클래스에서 오버라이드할 수 없습니다.
  • 메소드를 final로 선언하면 해당 메소드의 동작을 변경할 수 없도록 합니다.
  • 보안 및 디자인 패턴에서 메소드의 일관성을 유지하기 위해 사용됩니다.
Java
public class Parent {
    public final void finalMethod() {
        // 메소드의 내용
    }
}

public class Child extends Parent {
    // 아래의 시도는 오류를 발생시킴
    // @Override
    // public void finalMethod() { /* 오류 발생! final 메소드는 오버라이드할 수 없음 */ }
}
Java

final 클래스

  • final로 선언된 클래스는 상속될 수 없습니다.
  • 클래스를 final로 선언하면 다른 클래스에서 이를 상속받을 수 없어서, 클래스의 구현이나 동작을 변경하지 못하게 합니다.
  • 예를 들어, 라이브러리 클래스의 안정성을 보장하거나, 특정 클래스의 변경을 원하지 않을 때 사용됩니다.
Java
public final class FinalClass {
    // 클래스의 내용
}

// 아래의 시도는 오류를 발생시킴
// class SubClass extends FinalClass { /* 오류 발생! final 클래스는 상속할 수 없음 */ }
Java

final은 코드의 안정성과 유지보수성을 높이기 위해 사용되며, 클래스, 메소드, 변수에 적용하여 의도치 않은 변경을 방지합니다. 다만, 적절한상황에서 사용해야 하며, 남용하지 않아야 합니다.

인터페이스(Interface)

자바에서의 인터페이스(Interface)는 객체 지향 프로그래밍에서 사용되는 중요한 개념 중 하나로, 추상화를 제공하고 다중 상속을 지원하기 위한 메커니즘입니다. 인터페이스는 클래스와는 달리 멤버 변수를 가질 수 없으며, 추상 메소드, 상수, 디폴트 메소드, 정적 메소드 등을 정의할 수 있습니다.

인터페이스 정의

  • 인터페이스는 interface 키워드를 사용하여 정의됩니다. 추상 메소드, 디폴트 메소드, 정적 메소드, 상수 등을 선언할 수 있습니다.
  • 인터페이스는 추상 메소드를 선언할 수 있습니다. 이 메소드들은 본체가 없으며, 구현 클래스에서 반드시 구현되어야 합니다.
  • 인터페이스에서 선언한 변수는 상수로 취급되며, 자동으로 public, static, final 속성을 갖습니다. 이는 변경되지 않아야 하는 상수값에 유용합니다.
Java
public interface MyInterface {
    // 추상 메소드
    void abstractMethod();

    // 디폴트 메소드
    default void defaultMethod() {
        System.out.println("Default implementation");
    }

    // 정적 메소드
    static void staticMethod() {
        System.out.println("Static method implementation");
    }

    // 상수
    int CONSTANT_VALUE = 42;
}
Java

인터페이스 구현

  • 인터페이스를 구현하는 클래스는 implements 키워드를 사용하여 구현합니다. 모든 추상 메소드를 반드시 구현해야 합니다.
Java
public class MyClass implements MyInterface {
    @Override
    public void abstractMethod() {
        System.out.println("Abstract method implementation");
    }
}
Java

다중 인터페이스 구현

  • 클래스는 여러 개의 인터페이스를 동시에 구현할 수 있습니다.
Java
public class MultiInterfaceClass implements MyInterface, AnotherInterface {
    @Override
    public void abstractMethod() {
        System.out.println("Abstract method implementation");
    }

    // AnotherInterface의 메소드 구현
    @Override
    public void anotherMethod() {
        System.out.println("Another method implementation");
    }
}
Java

디폴트 메소드 사용

  • 디폴트 메소드는 인터페이스에서 일반적인 구현을 제공하는 데 사용됩니다. 구현 클래스에서 이를 그대로 사용하거나 필요에 따라 재정의할 수 있습니다.
Java
public class MyClass implements MyInterface {
    @Override
    public void abstractMethod() {
        System.out.println("Abstract method implementation");
    }

    // MyInterface의 디폴트 메소드를 사용하거나 재정의
    @Override
    public void defaultMethod() {
        System.out.println("Custom default method implementation");
    }
}
Java

정적 메소드 사용

  • 정적 메소드는 인터페이스 이름을 통해 직접 호출됩니다.
MyInterface.staticMethod(); // 정적 메소드 호출

다형성 활용

  • 인터페이스는 다형성을 지원하므로, 인터페이스를 구현한 다양한 클래스 객체를 동일한 인터페이스 타입으로 다룰 수 있습니다.
MyInterface myObject = new MyClass();
myObject.abstractMethod();  // 다형성을 통한 메소드 호출

인터페이스는 자바에서 코드의 재사용성을 높이고, 유연한 설계를 가능케 하는 강력한 도구입니다. 인터페이스를 활용하면 다양한 클래스가 공통된 규약을 따르도록 강제할 수 있고, 다중 상속을 통해 유연한 구조를 형성할 수 있습니다.

마무리

이번 글에서는 객체 지향 프로그래밍의 중요한 개념 중 하나인 다형성에 대해 다뤄보았습니다. 개념 학습 뿐만 아니라 실제 코드 작성을 통해 익숙해지는 경험이 중요하다는 것을 느꼈습니다. 다형성은 메소드 오버로딩과 오버라이딩을 통해 구현되며, 업캐스팅과 다운캐스팅을 통해 객체를 유연하게 다룰 수 있습니다.

추상 클래스는 공통된 특징을 제공하고 메소드의 일부를 구현하는데 사용되며, 상속을 통한 다형성을 활용하여 유연하고 확장 가능한 코드를 작성할 수 있습니다.

final 키워드는 변수, 메소드, 클래스에 적용되어 최종적이거나 변경될 수 없음을 나타내며, 코드의 안정성과 유지보수성을 높이기 위해 사용됩니다.

인터페이스는 다중 상속을 지원하고 추상화를 제공하여 유연한 설계를 가능하게 하는 중요한 개념입니다. 인터페이스를 통해 다형성을 활용하여 코드의 재사용성을 높일 수 있습니다.

이러한 다형성, 추상 클래스, final, 인터페이스에 대한 개념은 객체 지향 프로그래밍을 이해하고 유연한 코드를 작성하는 데 도움이 되는 중요한 개념들입니다. 앞으로 이러한 개념을 심화학습하고 실제 프로젝트에 적용해보며 더 나은 개발자로 성장해 나가려 합니다.

Leave a Comment