Javaのenum(列挙型)とは、「決まった値の集まり」を型として定義する仕組みです。 enum Size { SMALL, MEDIUM, LARGE } と書くと、Size 型の変数には3つの定数のどれかしか入らなくなります。文字列や int の定数と違い、書き間違いや範囲外の値をコンパイル時に見つけられるのが大きな利点です。

enumは定数を並べるだけでなく、フィールド・コンストラクタ・メソッドを持つ立派なクラスです。この記事では、基本メソッドから、コード値からの逆引き、switch式(Java 14以降)とJava 21のパターンマッチ、EnumMap・EnumSet、状態遷移まで、実務でよく使う10のパターンを、Java 21(OpenJDK 21.0.10)の単体ファイル実行(java X.java)で動かした実行結果つきで紹介します。valueOf の例外メッセージや、ordinal を保存したときの挙動も実際に確認しています。

Java enumの早見表

やりたいこと 書き方 結果・ポイント
定義する enum Size { SMALL, MEDIUM, LARGE } 定数名は大文字とアンダースコアが慣例
全定数を取得 Size.values() 定義順の配列(呼ぶたびに新しい配列)
文字列 → 定数 Size.valueOf("LARGE") 一致しないと IllegalArgumentException
定数 → 文字列 s.name() 定義した名前そのもの(toString() は上書き可)
定義順の番号 s.ordinal() 0始まり。保存や計算には使わない
比較 s == Size.SMALL == で比べてよい(null でも例外にならない)
値を持たせる SMALL("S") + フィールド+コンストラクタ コンストラクタは暗黙に private
定数ごとに処理を変える 抽象メソッド、またはラムダのフィールド if/switch の分岐を減らせる
コード値から逆引き static Map を作って get 見つからないときは Optional で返す
分岐 switch 式(Java 14以降) 全定数を書けば default 不要
集合・Map EnumSet、EnumMap HashSet・HashMap より軽く、定義順に並ぶ
共通の型で扱う enum X implements Y 継承(extends)はできない
シングルトン enum X { INSTANCE; } 1つだけのインスタンスを簡潔に作れる
実務パターン10選 見出し
1. 基本メソッドと比較 基本の使い方
2. ラベル・コード値を持たせる フィールドとコンストラクタ
3. コード値から逆引き Mapで逆引き
4. 定数ごとの振る舞い 抽象メソッド・ラムダ
5. switch式で分岐 switch式
6. Java 21のパターンマッチ パターンマッチ
7. EnumSet・EnumMap 集合とMap
8. インターフェース実装 共通の型
9. シングルトン 定数1つのenum
10. 状態遷移 移れる状態を定義

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「Java enumの使い方|実務パターン10選とswitch式・逆引きの書き方」

1. enumの基本メソッドと比較

enumには values()・valueOf()・name()・ordinal() が自動で用意されます。 比較は == で行えます。

import java.util.Arrays;

public class EnumBasics {
    enum Size { SMALL, MEDIUM, LARGE }

    public static void main(String[] args) {
        Size s = Size.MEDIUM;
        System.out.println("toString  : " + s);
        System.out.println("name()    : " + s.name());
        System.out.println("ordinal() : " + s.ordinal());
        System.out.println("values()  : " + Arrays.toString(Size.values()));
        System.out.println("valueOf() : " + Size.valueOf("LARGE"));
        System.out.println("==        : " + (s == Size.valueOf("MEDIUM")));
        System.out.println("compareTo : " + Size.SMALL.compareTo(Size.LARGE));
        System.out.println("class     : " + s.getDeclaringClass().getSimpleName());
        try {
            Size.valueOf("medium");
        } catch (IllegalArgumentException e) {
            System.out.println("例外      : " + e.getMessage());
        }
        try {
            Size.valueOf(null);
        } catch (NullPointerException e) {
            System.out.println("例外      : " + e.getMessage());
        }
    }
}

実行結果:

toString  : MEDIUM
name()    : MEDIUM
ordinal() : 1
values()  : [SMALL, MEDIUM, LARGE]
valueOf() : LARGE
==        : true
compareTo : -2
class     : Size
例外      : No enum constant EnumBasics.Size.medium
例外      : Name is null

valueOf は大文字・小文字を区別し、一致しない名前には IllegalArgumentException(No enum constant ...)、null には NullPointerException を投げます。 画面や外部から受け取った文字列を変換するときは、後述の「逆引き」の形にしておくと扱いやすくなります。

比べ方 結果
s == Size.SMALL 定数は1つずつしか存在しないので == で正しく比べられる。s が null でも false
s.equals(Size.SMALL) 結果は同じ。ただし s が null だと NullPointerException
s.compareTo(other) 定義順(ordinal)の差。並べ替えに使われる

2. フィールドとコンストラクタで値を持たせる

画面に出すラベルや、DB・外部連携用のコード値は、enumのフィールドに持たせると一か所で管理できます。 3のパターン(逆引き)も同じコードに入れています。

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

public class OrderStatus {
    enum Status {
        RECEIVED("10", "受付済み"),
        SHIPPED("20", "発送済み"),
        DELIVERED("30", "配達完了"),
        CANCELED("90", "キャンセル");

        private final String code;
        private final String label;

        Status(String code, String label) { // コンストラクタは暗黙に private
            this.code = code;
            this.label = label;
        }
        String code() { return code; }
        String label() { return label; }

        // コード値 → 定数 の逆引き表(クラス初期化時に1回だけ作る)
        private static final Map<String, Status> BY_CODE =
                Arrays.stream(values()).collect(Collectors.toUnmodifiableMap(Status::code, Function.identity()));

        static Optional<Status> fromCode(String code) {
            return Optional.ofNullable(BY_CODE.get(code));
        }
        static Status fromCodeOrThrow(String code) {
            return fromCode(code).orElseThrow(
                    () -> new IllegalArgumentException("未定義のステータスコード: " + code));
        }
    }

    public static void main(String[] args) {
        for (Status s : Status.values()) {
            System.out.println(s + " code=" + s.code() + " label=" + s.label());
        }
        System.out.println(Status.fromCode("20"));
        System.out.println(Status.fromCode("99"));
        System.out.println(Status.fromCode("99").map(Status::label).orElse("不明"));
        try {
            Status.fromCodeOrThrow("99");
        } catch (IllegalArgumentException e) {
            System.out.println("例外: " + e.getMessage());
        }
    }
}

実行結果:

RECEIVED code=10 label=受付済み
SHIPPED code=20 label=発送済み
DELIVERED code=30 label=配達完了
CANCELED code=90 label=キャンセル
Optional[SHIPPED]
Optional.empty
不明
例外: 未定義のステータスコード: 99

フィールドは final にしておくと、定数の中身が途中で変わらず安心です。 enumの定数はアプリ全体で共有されるため、変更できる値を持たせる場合は注意が必要です。

3. コード値から定数を逆引きする

逆引きは values() をループで探しても書けますが、static final Map を1回だけ作っておくと、定数が増えても一定の速さで引けます。 上のコードの BY_CODE がそれです。

逆引きの書き方 特徴
values() をループして一致を探す 短く書ける。定数が少なく呼び出しも少ないなら十分
static final Map を作って get 呼び出しが多いときに向く。コードの重複もMap作成時に検出できる
見つからないとき Optional を返す 呼び出し側が「ない場合」を必ず考える形になる
見つからないとき例外を投げる 不正データとして止めたい場面向け。メッセージに値を入れる

コンストラクタの中で static のMapに登録しようとすると、次のコンパイルエラーになります(実際の出力)。

enum Status {
    A("1"), B("2");
    private static final Map<String, Status> MAP = new HashMap<>();
    Status(String code) { MAP.put(code, this); }
}
StaticRefNg.java:6: error: illegal reference to static field from initializer
        Status(String code) { MAP.put(code, this); }
                              ^

enumでは定数(=コンストラクタ呼び出し)がstaticフィールドより先に初期化されるため、コンストラクタからstaticフィールドは参照できません。 上のコードのように、定数の宣言の後ろで values() からMapを作るのが定番の書き方です。

4. 定数ごとに振る舞いを変える

「定数によって計算方法が違う」ときは、switchで分岐するより、enum自身に処理を持たせると追加・修正が一か所で済みます。 書き方は2通りあります。

import java.util.function.IntUnaryOperator;

public class PerConstant {
    // 書き方1:定数ごとに抽象メソッドを実装する(定数固有のクラス本体)
    enum Shipping {
        STANDARD { int fee(int weightG) { return 500; } },
        EXPRESS  { int fee(int weightG) { return 800 + weightG / 1000 * 100; } },
        PICKUP   { int fee(int weightG) { return 0; } };

        abstract int fee(int weightG);
    }

    // 書き方2:ラムダをフィールドに持たせる
    enum Rounding {
        FLOOR(x -> x / 10 * 10),
        CEIL(x -> (x + 9) / 10 * 10),
        NONE(x -> x);

        private final IntUnaryOperator op;
        Rounding(IntUnaryOperator op) { this.op = op; }
        int apply(int x) { return op.applyAsInt(x); }
    }

    public static void main(String[] args) {
        for (Shipping s : Shipping.values()) {
            System.out.println(s + " 2500g -> " + s.fee(2500) + "円");
        }
        for (Rounding r : Rounding.values()) {
            System.out.println(r + " 123 -> " + r.apply(123));
        }
        System.out.println(Shipping.EXPRESS.getClass() + " / " + Shipping.EXPRESS.getDeclaringClass());
    }
}

実行結果:

STANDARD 2500g -> 500円
EXPRESS 2500g -> 1000円
PICKUP 2500g -> 0円
FLOOR 123 -> 120
CEIL 123 -> 130
NONE 123 -> 123
class PerConstant$Shipping$2 / class PerConstant$Shipping

抽象メソッドを宣言すると、新しい定数を追加したときに実装を書き忘れるとコンパイルエラーで気づけます。 最後の行のとおり、本体を持つ定数は getClass() が匿名のサブクラス(Shipping$2)になります。enumの型を調べたいときは getDeclaringClass() を使います。

書き方 向いている場面
抽象メソッド+定数ごとの本体 処理が数行あり、定数ごとに読みやすく書きたい
ラムダをフィールドに持たせる 処理が1行で、定義を一覧で見渡したい
switch式で外に書く enumを変更できない、または処理が別の層の責務

5. switch式で分岐する(Java 14以降)

switch式は値を返せるswitchで、Java 14で正式になりました。 enumに対して全定数の case を書くと、default を書かなくてもコンパイルが通ります。Java 8世代では case LOW: ... break; の形(switch文)で書いていましたが、この形も今もそのまま使えます。

public class SwitchDemo {
    enum Priority { LOW, MIDDLE, HIGH }

    // switch式(Java 14以降):全定数を書けば default 不要
    static String label(Priority p) {
        return switch (p) {
            case LOW -> "低";
            case MIDDLE -> "中";
            case HIGH -> "高";
        };
    }

    // 複数の定数をまとめる・ブロックで yield する
    static int days(Priority p) {
        return switch (p) {
            case LOW, MIDDLE -> 7;
            case HIGH -> {
                System.out.println("  (HIGH は即日対応)");
                yield 1;
            }
        };
    }

    // Java 21:型パターンと when ガード。enum定数は修飾名で書ける
    static String describe(Object o) {
        return switch (o) {
            case Priority.HIGH -> "enum: 最優先";
            case Priority p when p.ordinal() < 2 -> "enum: 通常 " + p;
            case Priority p -> "enum: その他 " + p;
            case String s -> "文字列: " + s;
            case null -> "null";
            default -> "その他: " + o;
        };
    }

    public static void main(String[] args) {
        for (Priority p : Priority.values()) {
            System.out.println(p + " " + label(p) + " " + days(p) + "日");
        }
        for (Object o : new Object[] { Priority.HIGH, Priority.LOW, "memo", null, 42 }) {
            System.out.println(describe(o));
        }
    }
}

実行結果:

LOW 低 7日
MIDDLE 中 7日
  (HIGH は即日対応)
HIGH 高 1日
enum: 最優先
enum: 通常 LOW
文字列: memo
null
その他: 42

case HIGH を書き忘れると、次のコンパイルエラーで教えてくれます(実際の出力)。

SwitchNg.java:4: error: the switch expression does not cover all possible input values
        return switch (p) {
               ^

switch式で default を書かずにおくと、定数を追加したときに対応漏れの箇所をコンパイラがすべて示してくれます。 逆に default を書くと、新しい定数も黙って default に入ります。

6. Java 21のパターンマッチとenum

Java 21ではswitchのパターンマッチ(JEP 441)が正式になり、enum定数を Priority.HIGH のような修飾名で case に書けるようになりました。 上の describe のように、Object や sealed インターフェースを受けるswitchの中でもenum定数を直接扱えます。when で条件を追加でき、case null で null も分岐できます。

機能 バージョン 例
アロー形式の case とswitch式 Java 14で正式 case LOW -> "低";
yield で値を返す Java 14で正式 case HIGH -> { yield 1; }
型パターン・when ガード Java 21で正式 case Priority p when ...
enum定数の修飾名 Java 21で正式 case Priority.HIGH ->
case null Java 21で正式 case null -> "null";

7. EnumSet・EnumMapで集合とMapを扱う

キーや要素がenumなら、EnumSet と EnumMap を使うのが定番です。 Javadocによると、内部はビット列や配列で表現されるため HashSet・HashMap より軽く、反復は定義順になります。

import java.util.*;
import java.util.stream.Collectors;

public class EnumCollections {
    enum Day { MON, TUE, WED, THU, FRI, SAT, SUN }
    enum Category { STATIONERY, FURNITURE, DEVICE }
    record Item(String name, Category category) {}

    public static void main(String[] args) {
        // EnumSet:定数の集合(内部はビット列で高速)
        EnumSet<Day> weekend = EnumSet.of(Day.SAT, Day.SUN);
        EnumSet<Day> weekdays = EnumSet.complementOf(weekend);
        EnumSet<Day> midweek = EnumSet.range(Day.TUE, Day.THU);
        System.out.println("weekend  : " + weekend);
        System.out.println("weekdays : " + weekdays);
        System.out.println("midweek  : " + midweek);
        System.out.println("all/none : " + EnumSet.allOf(Day.class).size() + " / " + EnumSet.noneOf(Day.class));

        // EnumMap:キーが enum の Map(定義順で並ぶ)
        List<Item> items = List.of(new Item("机", Category.FURNITURE), new Item("ペン", Category.STATIONERY),
                new Item("ノート", Category.STATIONERY), new Item("椅子", Category.FURNITURE));
        Map<Category, Long> count = items.stream().collect(
                Collectors.groupingBy(Item::category, () -> new EnumMap<>(Category.class), Collectors.counting()));
        System.out.println("count    : " + count);
        EnumMap<Category, Integer> stock = new EnumMap<>(Category.class);
        stock.put(Category.DEVICE, 3);
        stock.put(Category.STATIONERY, 120);
        System.out.println("stock    : " + stock);
    }
}

実行結果:

weekend  : [SAT, SUN]
weekdays : [MON, TUE, WED, THU, FRI]
midweek  : [TUE, WED, THU]
all/none : 7 / []
count    : {STATIONERY=2, FURNITURE=2}
stock    : {STATIONERY=120, DEVICE=3}

stock は DEVICE を先に入れましたが、表示は定義順(STATIONERY → DEVICE)です。 集計結果を画面に出すとき、順番を別に考えなくて済みます。

メソッド 意味
EnumSet.of(a, b) 指定した定数の集合
EnumSet.allOf(X.class) / noneOf(X.class) 全部/空
EnumSet.range(from, to) 定義順で from から to まで(両端を含む)
EnumSet.complementOf(set) set に含まれないもの
new EnumMap<>(X.class) キーが enum のMap。null キーは不可

8. インターフェースを実装して共通の型で扱う

enumは他のクラスを継承できませんが、インターフェースは実装できます。 別々のenumを同じ型で扱えるので、選択肢の一覧表示などを共通化できます。9のシングルトンも同じコードに入れています。

import java.util.*;

public class EnumInterface {
    interface Labeled { String label(); }

    enum TaskState implements Labeled {
        TODO("未着手"), DOING("作業中"), DONE("完了");
        private final String label;
        TaskState(String label) { this.label = label; }
        @Override public String label() { return label; }
    }

    enum MemberRank implements Labeled {
        REGULAR, SILVER, GOLD;
        @Override public String label() { return name().charAt(0) + name().substring(1).toLowerCase(); }
    }

    // 別々の enum を同じインターフェースで扱える
    static String options(Labeled[] values) {
        return String.join(" / ", Arrays.stream(values).map(Labeled::label).toList());
    }

    // シングルトン:定数1つの enum
    enum IdGenerator {
        INSTANCE;
        private int next = 1;
        synchronized String nextId(String prefix) { return prefix + "-" + String.format("%04d", next++); }
    }

    public static void main(String[] args) {
        System.out.println(options(TaskState.values()));
        System.out.println(options(MemberRank.values()));
        System.out.println(IdGenerator.INSTANCE.nextId("ORD"));
        System.out.println(IdGenerator.INSTANCE.nextId("ORD"));
    }
}

実行結果:

未着手 / 作業中 / 完了
Regular / Silver / Gold
ORD-0001
ORD-0002

9. 定数1つのenumでシングルトンを作る

enum IdGenerator { INSTANCE; } のように定数を1つだけ持つenumは、インスタンスが1つだけであることを言語が保証します。 リフレクションやシリアライズでも2つ目は作られません。上の例のように状態を持たせる場合は、複数のスレッドから呼ばれることを考えて synchronized などで守ります。Spring Bootなどのフレームワークを使う場合は、シングルトンの管理をDIコンテナに任せるのが一般的です。

10. 状態遷移を定義する

「どの状態からどの状態へ移れるか」をenumに持たせると、ルールが一か所にまとまります。

import java.util.*;

public class StateMachine {
    enum Status {
        DRAFT, REVIEW, PUBLISHED, ARCHIVED;

        private Set<Status> next;
        static { // 全定数ができた後に遷移先を設定する
            DRAFT.next = EnumSet.of(REVIEW);
            REVIEW.next = EnumSet.of(DRAFT, PUBLISHED);
            PUBLISHED.next = EnumSet.of(ARCHIVED);
            ARCHIVED.next = EnumSet.noneOf(Status.class);
        }
        boolean canMoveTo(Status to) { return next.contains(to); }
        Status moveTo(Status to) {
            if (!canMoveTo(to)) throw new IllegalStateException(this + " から " + to + " へは移れません");
            return to;
        }
    }

    public static void main(String[] args) {
        Status s = Status.DRAFT;
        s = s.moveTo(Status.REVIEW);
        s = s.moveTo(Status.PUBLISHED);
        System.out.println("現在: " + s);
        try {
            s.moveTo(Status.DRAFT);
        } catch (IllegalStateException e) {
            System.out.println("例外: " + e.getMessage());
        }
    }
}

実行結果:

現在: PUBLISHED
例外: PUBLISHED から DRAFT へは移れません

遷移先は、まだ作られていない定数を参照するためコンストラクタでは渡せません。 そこで static 初期化ブロックで、全定数ができた後に設定しています。

ordinalを保存すると何が起きるか

ordinal() は定義順の番号なので、途中に定数を追加すると番号がずれます。 DBやファイルに ordinal を保存していると、読み戻したときに別の定数になります。

public class OrdinalPitfall {
    // 最初の定義
    enum StatusV1 { RECEIVED, SHIPPED, DELIVERED }
    // あとから途中に PACKED を追加した定義
    enum StatusV2 { RECEIVED, PACKED, SHIPPED, DELIVERED }

    public static void main(String[] args) {
        int saved = StatusV1.SHIPPED.ordinal();          // DBに ordinal を保存していた
        System.out.println("保存した値 : " + saved);
        System.out.println("V2で読むと : " + StatusV2.values()[saved]); // 別の定数になる

        String savedName = StatusV1.SHIPPED.name();      // name を保存していた場合
        System.out.println("nameで読む : " + StatusV2.valueOf(savedName));
    }
}

実行結果:

保存した値 : 1
V2で読むと : PACKED
nameで読む : SHIPPED

保存するなら name()、または2で紹介した専用のコード値にすると、定数の追加や並べ替えに強くなります。JPAでは @Enumerated の既定が ORDINAL なので、@Enumerated(EnumType.STRING) を指定するか、AttributeConverter でコード値に変換する形がよく使われます(JPAは本記事では未実行、Jakarta Persistenceの仕様に基づく記載です)。

enumとsealed interface+recordの使い分け

Java 17以降は、sealed interface(実装できるクラスを限定するインターフェース)とrecordでも「決まった種類」を表せます。 種類ごとに持つデータが違うなら sealed、データの形が同じならenumが向いています。

import java.util.List;

public class SealedVsEnum {
    // 種類ごとに持つデータが違う → sealed interface + record
    sealed interface Discount permits Percent, Amount, NoDiscount {}
    record Percent(int rate) implements Discount {}
    record Amount(int yen) implements Discount {}
    enum NoDiscount implements Discount { INSTANCE } // データのない種類は enum でもよい

    static int apply(int price, Discount d) {
        return switch (d) { // sealed なので default 不要(網羅性チェック)
            case Percent p -> price * (100 - p.rate()) / 100;
            case Amount a -> Math.max(price - a.yen(), 0);
            case NoDiscount n -> price;
        };
    }

    public static void main(String[] args) {
        for (Discount d : List.of(new Percent(20), new Amount(300), NoDiscount.INSTANCE)) {
            System.out.println(d + " -> " + apply(1000, d));
        }
    }
}

実行結果:

Percent[rate=20] -> 800
Amount[yen=300] -> 700
INSTANCE -> 1000
比べる点 enum sealed interface+record
種類の数 固定(定数の数だけ) 固定(permits の型の数だけ)
インスタンス 定数ごとに1つだけ 何個でも作れる(new Percent(10)、new Percent(20))
種類ごとのデータ 全定数で同じ形のフィールド 種類ごとに違う形を持てる
values() で全部を列挙 できる できない
EnumSet・EnumMap・name() での保存 できる できない(自前で変換)
switchの網羅性チェック できる できる(Java 21)
向いている例 ステータス、区分、曜日、権限 割引の種類、通知の種類、イベントの種類

よくある質問

Q. enumはどのJavaバージョンから使えますか? A. Java 5(1.5)から使えます。 switch式はJava 14、switchのパターンマッチと case Priority.HIGH の修飾名はJava 21で正式になりました。フィールドやコンストラクタ、EnumMap・EnumSet はJava 5から使える機能です。

Q. enumの比較は == と equals のどちらが良いですか? A. == で問題ありません。 各定数はJVM内に1つだけなので結果は同じで、== は左辺が null でも例外になりません。

Q. values() を何度も呼んでも大丈夫ですか? A. 呼ぶたびに配列のコピーが作られます。 ループの中で大量に呼ぶ場合は、static final List<X> ALL = List.of(values()); のように1回だけ作っておくと無駄がありません。

Q. 定数を後から追加するときに気をつけることは? A. ordinal に依存していないか、default 付きのswitchで対応が漏れていないかを確認します。 switch式を default なしで書いておけば、対応漏れはコンパイラが示してくれます。

まとめ

  • enumは「決まった値の集まり」を型にする仕組み。フィールド・コンストラクタ・メソッドも持てる
  • valueOf は一致しないと IllegalArgumentException。外部の値は static Map の逆引き+Optional で受ける
  • 定数ごとの違いは抽象メソッドかラムダのフィールドでenum自身に持たせる
  • switch式は default なしで全定数を書くと、定数追加時の対応漏れをコンパイラが示す
  • Java 21では case Priority.HIGH や when ガードでパターンマッチと組み合わせられる
  • 集合・Mapは EnumSet・EnumMap。定義順で並び、軽く扱える
  • 保存には ordinal ではなく name かコード値。種類ごとにデータが違うなら sealed+record

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「Java enumの使い方|実務パターン10選とswitch式・逆引きの書き方」

株式会社RJCでは、enumのような基本の言語機能も、現場のコードで「どう書くと読みやすいか」を仲間と話し合いながら学び合っています。こうした知識を共有しながら、日々技術を磨いています。

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?