LoginSignup
2
2

More than 3 years have passed since last update.

Javaで書かれたSpring APIをKotlinで書いて比較してみた

Last updated at Posted at 2020-11-30

前書き

今までSpring Bootを使ってJavaで書いていたAPIを、
Kotlinではどう書くのか調べて実装してみたので、その備忘録。

記事概要

いわゆる階層構造システムの基本となる、以下の実装について比較してみる。

  • Entity
  • Controller
  • Service
  • Repository

環境

  • Spring Boot 2.4.0
  • JavaコードはLombokを使用する

共通ポイント

Javaと比較した時のKotlinを理解する上での(個人的な)ポイントは、

  • val/varの意図(valは読み取り専用、varは読み書き両方可能)
  • デフォルトの修飾子はpublic
    • Javaでのpublic classclassのみで同義に
  • コンストラクタの書き方
    • コンストラクタに関しては色々な固有機能があるみたいなのですが、ここでは実装のための書き方のみ記します
  • カプセル化のためのgetter/setterは自動で作ってくれる(Lombokなどの外部ライブラリではなく仕様として)
  • 文末に;付けない

Entity

Java

import lombok.Data;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import java.time.LocalDateTime;

@Entity
@Table(name = "english_word")
@Data
public class EnglishWordJava {
  // ID
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;
  // 単語名
  private String word;
  // 意味
  private String meaning;
  // 作成日時
  @CreationTimestamp private LocalDateTime createTime;
  // 更新日時
  @UpdateTimestamp private LocalDateTime updateTime;
}

Kotlin

import org.hibernate.annotations.CreationTimestamp
import org.hibernate.annotations.UpdateTimestamp
import java.time.LocalDateTime
import javax.persistence.*

@Entity
@Table(name = "english_word")
data class EnglishWord(
        // ID
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        val id: Long = 0,
        // 単語名
        var word: String = "",
        // 意味
        var meaning: String = "",
        // 作成日時
        @CreationTimestamp val createTime: LocalDateTime = LocalDateTime.now(),
        // 更新日時
        @UpdateTimestamp
        val updateTime: LocalDateTime = LocalDateTime.now()
)

メモ

  • データクラスと言うKotlinにおける仕様を用いている。(data class ○○)
  • 初期値を明示している理由は、インスタンス生成時に引数を入れたくないため。(データクラスでは(色々記述しないと)全メンバを引数にとるコンストラクタのみしか生成されないため)
    • なお普通のクラスでも同様の実装は可能なのでお好みで良いかも
    • 明示的にval/varを分けているがそうする必要があるかはわかっていない。。

(おまけ)登録Form

わざわざ記載するほどのものではないですが一応。

Java

import lombok.Data;

@Data
public class EnglishWordJavaForm {
  // 単語名
  private String word;
  // 意味
  private String meaning;
}

Kotlin

data class EnglishWordForm(
        // 単語名
        val word: String,
        // 意味
        val meaning: String
)

Controller

Java

import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/* 自作クラスのimportは割愛 */

@RestController
@RequestMapping(path = "/api")
@RequiredArgsConstructor
public class EnglishWordJavaController {

  private EnglishWordJavaService englishWordJavaService;

  /** 取得API */
  @GetMapping("/word")
  @ResponseStatus(HttpStatus.OK)
  public List<EnglishWordJava> getEnglishWord() {
    return englishWordJavaService.getEnglishWord();
  }

  /** 登録API */
  @PostMapping("/word")
  @ResponseStatus(HttpStatus.CREATED)
  public void registerEnglishWord(@RequestBody EnglishWordJavaForm englishWordJavaForm) {
    englishWordJavaService.registerEnglishWord(englishWordJavaForm);
  }
}

Kotlin

import org.springframework.http.HttpStatus
import org.springframework.web.bind.annotation.*
/* 自作クラスのimportは割愛 */

@RestController
@RequestMapping(path = ["/api"])
class EnglishWordController(private val englishWord: EnglishWordService) {

    /**
     * 取得API
     */
    @GetMapping("/word")
    @ResponseStatus(HttpStatus.OK)
    fun getEnglishWord(): List<EnglishWord> {
        return englishWordService.getEnglishWord()
    }

    /**
     * 登録API
     */
    @PostMapping("/word")
    @ResponseStatus(HttpStatus.CREATED)
    fun registerEnglishWord(@RequestBody englishWordForm: EnglishWordForm) {
        topService.registerEnglishWord(englishWordForm)
    }
}

メモ

  • サービス呼ぶ処理が簡潔になった
  • voidを表現するには、型を指定しないだけで良いよう

Service

Java

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.List;
/* 自作クラスのimportは割愛 */

@Service
@RequiredArgsConstructor
public class EnglishWordJavaService {

  private EnglishWordJavaRepository englishWordJavaRepository;

  /** データを全て取得 */
  public List<EnglishWordJava> getEnglishWord() {
    return englishWordJavaRepository.findAll();
  }

  /** 登録 */
  public void registerEnglishWord(EnglishWordJavaForm englishWordJavaForm) {
    EnglishWordJava englishWordJava = new EnglishWordJava();
    englishWordJava.setWord(englishWordJavaForm.getWord());
    englishWordJava.setMeaning(englishWordJavaForm.getMeaning());
    englishWordJavaRepository.save(englishWordJava);
  }
}

Kotlin

import org.springframework.stereotype.Service
/* 自作クラスのimportは割愛 */

@Service
class EnglishWordService(private val englishWordRepository: EnglishWordRepository) {

    /**
     * データを全て取得
     */
    fun getEnglishWord(): List<EnglishWord> = englishWordRepository.findAll()

    /**
     * 登録
     */
    fun registerEnglishWord(englishWordForm: EnglishWordForm) {
        val englishWord = EnglishWord()
        englishWord.word = englishWordForm.word
        englishWord.meaning = englishWordForm.meaning
        englishWordRepository.save(englishWord)
    }
}

メモ

  • 関数を定義する際、処理が1行のみなら波括弧{}を省略して書ける
  • getter/setterの呼び出し方が若干違う

Repository

Java

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
/* 自作クラスのimportは割愛 */

@Repository
public interface EnglishWordJavaRepository extends JpaRepository<EnglishWordJava, Long> {}

Kotlin

import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository
/* 自作クラスのimportは割愛 */

@Repository
interface EnglishWordRepository : JpaRepository<EnglishWord, Long> {}

後書き

ざっくりとSpring Bootを使ってKotlinでAPI実装の記述をしてみました。
まだ使って間もないので、Kotlin特有の機能などもっと調べて記事にできたらいいなと思ってます。

記述量を大幅に減らせるので、慣れたら大分楽になる印象。
性能面でもいつか比較してみたいな(願望)

参考

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2