ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 14. 자바 인터페이스와 다형성 (1)
    자바(Java) 강의 2019. 4. 22. 09:18

    이 포스트에서는 인터페이스와 인터페이스로 다형성을 어떻게 구현하는지에 알아보도록 하겠다.

    이전 포스트

    목표

    • 프로젝트 구조
    • 인터페이스가 필요한 이유
    • 인터페이스 (Interface)
    • 상수 (Constant)

    프로젝트 구조

    JavaInterfaceTutorial

    ├── JavaInterfaceTutorial.iml

    └── src

        ├── AgeUserValidator.java

        ├── Main.java

        ├── NameUserValidator.java

        ├── User.java

        └── UserValidator.java


    1 directory, 6 files

    인터페이스가 필요한 이유

    예를들어 아래처럼 User라는 클래스가 있다고하자. 우리는 인풋으로 name과 age를 입력받고, 입력받은 값이 올바른지 확인하고싶다.

    public class User {
    public String name;
    public int age;
    }

    간단하게 이름은 0자 이상이어야하고 나이는 1~99세까지라고 해보자. 지금까지 알고있는 지식으로는 아래처럼 검사를 하는 코드를 짤 수 있다.

    import java.util.Scanner;

    public class Main {
    public static void main(String[] args) {
    User user = new User();
    System.out.print("## 이름 :");
    user.name = new Scanner(System.in).next();
    System.out.print("## 나이 :");
    user.age = new Scanner(System.in).nextInt();
    validateUser(user);
    }

    public static void validateUser(User user) {
    if(user.name == null || user.name.isEmpty()) {
    System.out.println("에러: 이름을 입력해주세요.");
    }

    if(user.age <= 0 || user.age > 99) {
    System.out.println("에러: 올바른 나이를 입력해주세요.");
    }
    }
    } //실행결과

    ## 이름 :d ## 나이 :-1 에러: 올바른 나이를 입력해주세요.

    위처럼 짤 수 있다. 하지만 만약에 유저에 10개의 멤버변수가 있고 각 멤버변수를 모두 검사해야 한다면 어떻게 하겠는가? 또는 다른 메서드에서는 name만 검사하고 age는 검사하지 않고 싶다면 어떻게 할것인가? 일단 이 메서드들을 나눠야한다.

    import java.util.Scanner;

    public class Main {
    public static void main(String[] args) {
    User user = new User();
    System.out.print("## 이름 :");
    user.name = new Scanner(System.in).next();
    System.out.print("## 나이 :");
    user.age = new Scanner(System.in).nextInt();
    validateName(user);
    validateAge(user);
    }

    public static void validateName(User user) {
    if(user.name == null || user.name.isEmpty()) {
    System.out.println("에러: 이름을 입력해주세요.");
    }
    }
    public static void validateAge(User user) {
    if(user.age <= 0 || user.age > 99) {
    System.out.println("에러: 올바른 나이를 입력해주세요.");
    }
    }
    }

    이렇게하면 원하는 멤버변수를 검사하고싶을 때마다 원하는 메서드를 부르면 된다. 이렇게 하는경우 10개의 validate메서드를 만들고 validate을 원하는곳에서 부를수있다. 하지만 이렇게 10개가 있으면 코드를 관리하기 매우 어려워진다. 또 유저뿐만 아니라 프로그램을 다른것들도 validate해야 할 수 있다. 그런경우 이렇게 한 클래스에서 메서드를 여러개 작성하는것은 개발자를 매우 헷갈리게 할 수 있다.

    인터페이스 (Interface)

    인터페이스는 추상클래스와 비슷하다. 단 변수를 가질 수 없으며 추상메서드만 있어야 한다. (자바 8에서는 default메서드를 제공하지만 이는 자바8에서 다루도록 한다.) 이 포스트에서는 Validator라는 인터페이스를 예를들어 설명할 것이니, User.java와 UserValidator.java를 만들어보도록 하자.

    public interface UserValidator {

    }

    인터페이스는 위처럼 <접근제어자> interface <인터페이스_이름>으로 생성할 수 있다. 위에서 말했듯 인터페이스는 상수 또는 추상메서드만 가질 수 있다. 추상메서드는 추상 클래스에서 했으니 UserValidator에 validate이라는 추상메서드를 선언 해 보자.

    public interface UserValidator {
    void validate(User user);
    }

    이렇게 만든 인터페이스를 사용하는것을 '구현(implement)한다'라고한다. 이제 각 Name, Age를 검사하기위한 클래스를 각각 만들어보자.

    NameUserValidator.java

    public class NameUserValidator implements UserValidator {
    @Override
    public void validate(User user) {
    if(user.name == null || user.name.isEmpty()) {
    System.out.println("에러: 이름을 입력해주세요.");
    }
    }
    }

     구현은 클래스의 이름 다음에 implements <인터페이스이름>을 적어준다. 그리고나서 validate메서드를 Override해 구현해 주면 된다.

    AgeUserValidator.java

    public class AgeUserValidator implements UserValidator {
    @Override
    public void validate(User user) {
    if(user.age <= 0 || user.age > 99) {
    System.out.println("에러: 올바른 나이를 입력해주세요.");
    }
    }
    }

    이렇게 만든 Validator들을 어떻게 사용할까?

    import java.util.Scanner;

    public class Main {

    static UserValidator[] userValidator = {new NameUserValidator(), new AgeUserValidator()};

    public static void main(String[] args) {
    User user = new User();
    System.out.print("## 이름 :");
    user.name = new Scanner(System.in).next();
    System.out.print("## 나이 :");
    user.age = new Scanner(System.in).nextInt();

    for (int i = 0; i < userValidator.length; i++) {
    userValidator[i].validate(user);
    }
    }
    } //실행 결과 ## 이름 :d ## 나이 :-1 에러: 올바른 나이를 입력해주세요.

    위 처럼 Main에서 UserValidator를 정의한후 위처럼 배열에 UserValidator가 있는 만큼 반복문을 돌려주면 10개의 메서드를 부르지 않아도 Validation을 할 수 있다. 이렇게하면 Validator가 몇개이든 상관없고, 나중에 또 다른 UserValidator를 추가하고싶을 때 배열에만 추가하면된다. 이렇게 하면 validate이라는 메서드를 부르지만 각 메서드의 구현부는 NameUserValidator, AgeUserValidator, ...등등 다르다.

    상수 (Constant)

    변수는 값이 변할 수 있는 것이다. 상수(Constant)는 맨 처음에 초기화한 후 값을 절대 바꿀수 없는 것이다. 상수를 어떻게 선언하는가? final이라는 키워드를 이용해 선언한다.

    final int NUMBER = 3;

    예를들어 위처럼 final을 변수 앞에 적어주면 앞으로 이 number안의 값은 절대 바꿀수 없다.

    인터페이스가 상수만 가질수 있다는 것은 인터페이스 내에 선언된 모든 변수의 앞에 final이 붙어있다는 뜻이고 그 값을 아무도 변경할 수 없다는 뜻이다.

    final은 간단해보이지만 레퍼런스 개념과 섞이면 헷갈리기 쉬우므로 다른 포스트에서 더 다루도록 하겠다.

    이번 포스트에서는 인터페이스를 정의하는 법과 사용하는 법에 대해서 알아보았다. 보통 검사를 하는 메서드에서는 에러메시지와 함께 Exception을 던진다. 이에 대해서는 다른 포스트에서 설명하도록 하겠다.


    전체 코드는 https://gist.github.com/fsoftwareengineer/ee4c3eaeb4d33a2f55c39e818d161d0a 에서 확인할 수 있다.

    다음 포스트:14. 자바 인터페이스와 다형성 (2)

    댓글

f.software engineer @ All Right Reserved