Javaその他

Javaのジェネリクスを利用して処理の共通化をしてみた

Javaのジェネリクスを利用すると、同一のスーパークラスをもつサブクラスに対する同じ処理を、共通化することができる。

今回は、ジェネリクスを利用して、引数で受け取ったサブクラスのオブジェクトを生成し、そのスーパークラスの値を設定する処理を実装してみたので、そのサンプルプログラムを共有する。

前提条件

下記記事の「IntelliJ IDEA上でSpring Bootプロジェクトの読み込み」までの処理が完了していること。

IntelliJ IDEA上でGradleを使ってWeb画面のSpring Bootプロジェクトを作成してみたSpring Bootのプロジェクトを新規作成を「IntelliJ IDEA」のメニューから実施しようとしたところ、無料の「Commun...

サンプルプログラムの内容

作成したサンプルプログラムの構成は以下の通り。
サンプルプログラムの構成
なお、上記の赤枠のうち、「build.gradle」は今回変更したプログラムで、他は新規で作成したプログラムとなる。「DemoUtil.java」に、ジェネリクスを利用したメソッドが含まれている。

「build.gradle」の内容は以下の通り。lombokを利用するための設定を追加している。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
plugins {
id 'org.springframework.boot' version '2.1.7.RELEASE'
id 'java'
}
apply plugin: 'io.spring.dependency-management'
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
//lombokを利用するための設定
compileOnly 'org.projectlombok:lombok:1.18.10'
annotationProcessor 'org.projectlombok:lombok:1.18.10'
}
plugins { id 'org.springframework.boot' version '2.1.7.RELEASE' id 'java' } apply plugin: 'io.spring.dependency-management' group = 'com.example' version = '0.0.1-SNAPSHOT' sourceCompatibility = '1.8' repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' implementation 'org.springframework.boot:spring-boot-starter-web' testImplementation 'org.springframework.boot:spring-boot-starter-test' //lombokを利用するための設定 compileOnly 'org.projectlombok:lombok:1.18.10' annotationProcessor 'org.projectlombok:lombok:1.18.10' }
plugins {
	id 'org.springframework.boot' version '2.1.7.RELEASE'
	id 'java'
}

apply plugin: 'io.spring.dependency-management'

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	//lombokを利用するための設定
	compileOnly 'org.projectlombok:lombok:1.18.10'
	annotationProcessor 'org.projectlombok:lombok:1.18.10'
}

また、スーパークラス「DemoSuperObj.java」の内容は以下の通り。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
package com.example.demo;
import lombok.Data;
import java.time.LocalDateTime;
//@Dataアノテーションにより、各インスタンス変数の@Getter,@Setterや
//@ToStringアノテーションが使用可能になる。
@Data
public class DemoSuperObj {
/** 作成日時 */
private LocalDateTime createDateTime;
/** 作成ユーザーID */
private String createUserId;
/** 更新日時 */
private LocalDateTime updateDateTime;
/** 更新ユーザーID */
private String updateUserId;
/** 削除フラグ */
private String deleteFlg;
}
package com.example.demo; import lombok.Data; import java.time.LocalDateTime; //@Dataアノテーションにより、各インスタンス変数の@Getter,@Setterや //@ToStringアノテーションが使用可能になる。 @Data public class DemoSuperObj { /** 作成日時 */ private LocalDateTime createDateTime; /** 作成ユーザーID */ private String createUserId; /** 更新日時 */ private LocalDateTime updateDateTime; /** 更新ユーザーID */ private String updateUserId; /** 削除フラグ */ private String deleteFlg; }
package com.example.demo;

import lombok.Data;
import java.time.LocalDateTime;

//@Dataアノテーションにより、各インスタンス変数の@Getter,@Setterや
//@ToStringアノテーションが使用可能になる。
@Data
public class DemoSuperObj {

    /** 作成日時 */
    private LocalDateTime createDateTime;

    /** 作成ユーザーID */
    private String createUserId;

    /** 更新日時 */
    private LocalDateTime updateDateTime;

    /** 更新ユーザーID */
    private String updateUserId;

    /** 削除フラグ */
    private String deleteFlg;

}



さらに、サブクラス「DemoSubObj1.java」「DemoSubObj2.java」の内容は以下の通り。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
package com.example.demo;
import lombok.Data;
import lombok.EqualsAndHashCode;
//@EqualsAndHashCodeアノテーションは、警告の抑制のため付与
@Data
@EqualsAndHashCode(callSuper = true)
public class DemoSubObj1 extends DemoSuperObj {
/** ユーザーID */
private String userId;
/** ユーザー名 */
private String userName;
@Override
public String toString(){
StringBuilder sb = new StringBuilder();
sb.append("DemoSubObj1(");
sb.append("userId=");
sb.append(userId);
sb.append(", userName=");
sb.append(userName);
sb.append(", ");
sb.append(super.toString());
sb.append(")");
return sb.toString();
}
}
package com.example.demo; import lombok.Data; import lombok.EqualsAndHashCode; //@EqualsAndHashCodeアノテーションは、警告の抑制のため付与 @Data @EqualsAndHashCode(callSuper = true) public class DemoSubObj1 extends DemoSuperObj { /** ユーザーID */ private String userId; /** ユーザー名 */ private String userName; @Override public String toString(){ StringBuilder sb = new StringBuilder(); sb.append("DemoSubObj1("); sb.append("userId="); sb.append(userId); sb.append(", userName="); sb.append(userName); sb.append(", "); sb.append(super.toString()); sb.append(")"); return sb.toString(); } }
package com.example.demo;

import lombok.Data;
import lombok.EqualsAndHashCode;

//@EqualsAndHashCodeアノテーションは、警告の抑制のため付与
@Data
@EqualsAndHashCode(callSuper = true)
public class DemoSubObj1 extends DemoSuperObj {

    /** ユーザーID */
    private String userId;

    /** ユーザー名 */
    private String userName;

    @Override
    public String toString(){
        StringBuilder sb = new StringBuilder();
        sb.append("DemoSubObj1(");
        sb.append("userId=");
        sb.append(userId);
        sb.append(", userName=");
        sb.append(userName);
        sb.append(", ");
        sb.append(super.toString());
        sb.append(")");
        return sb.toString();
    }
}
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
package com.example.demo;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = true)
public class DemoSubObj2 extends DemoSuperObj {
/** ユーザー住所 */
private String userAddress;
@Override
public String toString(){
StringBuilder sb = new StringBuilder();
sb.append("DemoSubObj2(");
sb.append("userAddress=");
sb.append(userAddress);
sb.append(", ");
sb.append(super.toString());
sb.append(")");
return sb.toString();
}
}
package com.example.demo; import lombok.Data; import lombok.EqualsAndHashCode; @Data @EqualsAndHashCode(callSuper = true) public class DemoSubObj2 extends DemoSuperObj { /** ユーザー住所 */ private String userAddress; @Override public String toString(){ StringBuilder sb = new StringBuilder(); sb.append("DemoSubObj2("); sb.append("userAddress="); sb.append(userAddress); sb.append(", "); sb.append(super.toString()); sb.append(")"); return sb.toString(); } }
package com.example.demo;

import lombok.Data;
import lombok.EqualsAndHashCode;

@Data
@EqualsAndHashCode(callSuper = true)
public class DemoSubObj2 extends DemoSuperObj {

    /** ユーザー住所 */
    private String userAddress;

    @Override
    public String toString(){
        StringBuilder sb = new StringBuilder();
        sb.append("DemoSubObj2(");
        sb.append("userAddress=");
        sb.append(userAddress);
        sb.append(", ");
        sb.append(super.toString());
        sb.append(")");
        return sb.toString();
    }
}



また、「DemoUtil.java」の内容は以下の通り。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
package com.example.demo;
import java.time.LocalDateTime;
public class DemoUtil {
/**
* 引数で受け取ったクラスのオブジェクトを生成し、親クラスの各値を設定し返す
* @param clazz DemoSuperObjまたはそのサブクラス
* @param <T>
* @param userId ユーザーID
* @return 引数で受け取ったクラスのオブジェクト
*/
public static <T extends DemoSuperObj> T makeArgClassObj(
Class<T> clazz, String userId){
T obj = null;
try{
//第一引数で受け取ったクラスのオブジェクトを生成
obj = clazz.getDeclaredConstructor().newInstance();
//*** 生成したオブジェクトの親クラスの各値を設定
//作成日時
obj.setCreateDateTime(LocalDateTime.now());
//作成ユーザーID
obj.setCreateUserId(userId);
//更新日時
obj.setUpdateDateTime(obj.getCreateDateTime());
//更新ユーザーID
obj.setUpdateUserId(userId);
//削除フラグ
obj.setDeleteFlg("0");
}catch (Exception ex){
System.err.println(ex);
}
return obj;
}
}
package com.example.demo; import java.time.LocalDateTime; public class DemoUtil { /** * 引数で受け取ったクラスのオブジェクトを生成し、親クラスの各値を設定し返す * @param clazz DemoSuperObjまたはそのサブクラス * @param <T> * @param userId ユーザーID * @return 引数で受け取ったクラスのオブジェクト */ public static <T extends DemoSuperObj> T makeArgClassObj( Class<T> clazz, String userId){ T obj = null; try{ //第一引数で受け取ったクラスのオブジェクトを生成 obj = clazz.getDeclaredConstructor().newInstance(); //*** 生成したオブジェクトの親クラスの各値を設定 //作成日時 obj.setCreateDateTime(LocalDateTime.now()); //作成ユーザーID obj.setCreateUserId(userId); //更新日時 obj.setUpdateDateTime(obj.getCreateDateTime()); //更新ユーザーID obj.setUpdateUserId(userId); //削除フラグ obj.setDeleteFlg("0"); }catch (Exception ex){ System.err.println(ex); } return obj; } }
package com.example.demo;

import java.time.LocalDateTime;

public class DemoUtil {

    /**
     * 引数で受け取ったクラスのオブジェクトを生成し、親クラスの各値を設定し返す
     * @param clazz DemoSuperObjまたはそのサブクラス
     * @param <T>
     * @param userId ユーザーID
     * @return 引数で受け取ったクラスのオブジェクト
     */
    public static <T extends DemoSuperObj> T makeArgClassObj(
                                   Class<T> clazz, String userId){
        T obj = null;
        try{
            //第一引数で受け取ったクラスのオブジェクトを生成
            obj = clazz.getDeclaredConstructor().newInstance();

            //*** 生成したオブジェクトの親クラスの各値を設定
            //作成日時
            obj.setCreateDateTime(LocalDateTime.now());
            //作成ユーザーID
            obj.setCreateUserId(userId);
            //更新日時
            obj.setUpdateDateTime(obj.getCreateDateTime());
            //更新ユーザーID
            obj.setUpdateUserId(userId);
            //削除フラグ
            obj.setDeleteFlg("0");
        }catch (Exception ex){
            System.err.println(ex);
        }
        return obj;
    }
}

Javaのジェネリクスを利用しており、第一引数にDemoSuperObjまたはそのサブクラスを渡し、そのクラスのオブジェクトを戻り値として返却するような仕組みになっている。

なお、ジェネリクスについての詳細は以下を参照のこと。
https://qiita.com/rodentia6/items/b36d134fa24867ba4d63



さらに、「DemoMain.java」の内容は以下の通り。DemoSubObj1のオブジェクトを生成して約1秒後に、DemoSubObj2のオブジェクトを生成している。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
package com.example.demo;
public class DemoMain {
public static void main(String[] args){
//DemoSubObj1クラス(=DemoSuperObjのサブクラス)のオブジェクトを生成
DemoSubObj1 obj1 = DemoUtil.makeArgClassObj(DemoSubObj1.class, "USER_1");
//DemoSubObj1クラスのオブジェクトのユーザーID・ユーザー名を設定
obj1.setUserId("USER_1");
obj1.setUserName("テスト プリン");
//1秒間だけ待機
try{
Thread.sleep(1000);
}catch(InterruptedException ex){
System.err.println(ex);
}
//DemoSubObj2クラス(=DemoSuperObjのサブクラス)のオブジェクトを生成
DemoSubObj2 obj2 = DemoUtil.makeArgClassObj(DemoSubObj2.class, "USER_2");
//DemoSubObj2クラスのオブジェクトの住所を設定
obj2.setUserAddress("●●県XX市");
//DemoSubObj1クラス, DemoSubObj2クラスの各オブジェクトの設定値を確認
System.out.println(obj1.toString());
System.out.println(obj2.toString());
}
}
package com.example.demo; public class DemoMain { public static void main(String[] args){ //DemoSubObj1クラス(=DemoSuperObjのサブクラス)のオブジェクトを生成 DemoSubObj1 obj1 = DemoUtil.makeArgClassObj(DemoSubObj1.class, "USER_1"); //DemoSubObj1クラスのオブジェクトのユーザーID・ユーザー名を設定 obj1.setUserId("USER_1"); obj1.setUserName("テスト プリン"); //1秒間だけ待機 try{ Thread.sleep(1000); }catch(InterruptedException ex){ System.err.println(ex); } //DemoSubObj2クラス(=DemoSuperObjのサブクラス)のオブジェクトを生成 DemoSubObj2 obj2 = DemoUtil.makeArgClassObj(DemoSubObj2.class, "USER_2"); //DemoSubObj2クラスのオブジェクトの住所を設定 obj2.setUserAddress("●●県XX市"); //DemoSubObj1クラス, DemoSubObj2クラスの各オブジェクトの設定値を確認 System.out.println(obj1.toString()); System.out.println(obj2.toString()); } }
package com.example.demo;

public class DemoMain {

    public static void main(String[] args){
        //DemoSubObj1クラス(=DemoSuperObjのサブクラス)のオブジェクトを生成
        DemoSubObj1 obj1 = DemoUtil.makeArgClassObj(DemoSubObj1.class, "USER_1");
        //DemoSubObj1クラスのオブジェクトのユーザーID・ユーザー名を設定
        obj1.setUserId("USER_1");
        obj1.setUserName("テスト プリン");

        //1秒間だけ待機
        try{
            Thread.sleep(1000);
        }catch(InterruptedException ex){
            System.err.println(ex);
        }

        //DemoSubObj2クラス(=DemoSuperObjのサブクラス)のオブジェクトを生成
        DemoSubObj2 obj2 = DemoUtil.makeArgClassObj(DemoSubObj2.class, "USER_2");
        //DemoSubObj2クラスのオブジェクトの住所を設定
        obj2.setUserAddress("●●県XX市");

        //DemoSubObj1クラス, DemoSubObj2クラスの各オブジェクトの設定値を確認
        System.out.println(obj1.toString());
        System.out.println(obj2.toString());
    }
}

「DemoMain.java」の実行結果は以下の通りで、スーパークラス「DemoSuperObj.java」の各値が設定されているのが確認できる。
サンプルプログラムの実行結果

要点まとめ

  • Javaのジェネリクスを利用すると、同一のスーパークラスをもつサブクラスに対する同じ処理を、共通化することができる。