본문 바로가기
Java/커먼의저주

[Java] @FunctionalInterface를 활용해서 깔끔한 코드 작성해보기

by 마라민초닭발로제 2025. 1. 23.

짧 주의

 

문제 상황: swift 처럼 callback함수를 통해 코딩하고 싶었는데 어떻게 할 방법이 없었음, 그래서 Function<T,R>이라는 함수를 사용했는데 다음과 같은 문제점이 있었음

1. callback에서 직접 throw를 처리해야함. 즉 callback에서 throw를 넘기지 못하는 문제가 발생ㅎ

2. 너무나 많은 try - catchblock때문에 정신없음

 

 

As is

실제 어지럽게 작성한 코드. Function<R,S>로 어거지로 해결하려다가 가독성 및 제대로 수행하지 못하고 에러가 난 모습을 볼 수 있습니다. 

private <T> T createPreparedStatement(String sql, Function<PreparedStatement, T> executor) throws SQLException {
    try (
            Connection conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);
            PreparedStatement prstmt = conn.prepareStatement(sql)
    ) {
        return executor.apply(prstmt);
    } catch (SQLException e) {
        e.printStackTrace();
        throw e; // 필요 시 사용자 정의 예외로 변환 가능
    }
}

// CREATE
public boolean addTodo(CreateTodoRequest dto) {
    String sql = tableType.insertTodoSQL(dto.userId(), dto.title(), dto.content());

    // Function 정의: PreparedStatement에서 execute()를 실행
    Function<PreparedStatement, Boolean> executeFunction = preparedStatement -> {
        try {
            return preparedStatement.execute();
        } catch (SQLException e) {
            System.err.println("Failed to execute SQL: " + preparedStatement);
            e.printStackTrace();
            return false; // 실패 시 안전하게 false 반환
        }
    };

    // 메서드 호출 및 예외 처리
    try {
        return createPreparedStatement(sql, executeFunction);
    } catch (SQLException e) {
        System.err.println("Failed to add todo: " + e.getMessage());
        return false;
    }
}

 

To be

@FuncationInterface를 통한 코드 Result Type과 <T> T를 정의해줘서 깔끔하게 리턴하는 모습을 볼 수 있습니다. 

// Custom functional interface to remove try-catch from executor
    // For
@FunctionalInterface
interface PreparedStatementExecutor<T> {
    T execute(PreparedStatement prstmt) throws SQLException;
}

private <T> T createPreparedStatement(String sql, PreparedStatementExecutor<T> executor) throws SQLException {
    try (
            Connection conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);
            PreparedStatement prstmt = conn.prepareStatement(sql);
    ) {
        return executor.execute(prstmt);
    }
}

// CREATE
public boolean addTodo(CreateTodoRequest dto) {
    String sql = tableType.insertTodoSQL(dto.userId(), dto.title(), dto.content());

    try {
        // Directly propagate SQLException
        return createPreparedStatement(sql, PreparedStatement::execute);
    } catch (SQLException e) {
        e.printStackTrace();
        return false;
    }
}