Javaジェネリック(ジェネリック+リフレクションのアプリケーションを含む)

ジェネリックス入門

Javaジェネリック(generics)は、JDK 5で導入された新機能です。ジェネリックは、编译时类型安全检测机制プログラマーがコンパイル時に不正な型を検出できるようにするメカニズムを提供します。

ジェネリックスの本質は参数化类型、つまり、操作されるデータのタイプが1つとして指定されること参数です。特定のタイプは、オブジェクトが作成されたとき、またはメソッドが呼び出されたときにのみ指定されます。

型消去

一般的な概念を正しく理解するための最初の前提条件は、を理解すること类型擦除です。

型消去の詳細については、次の記事を確認してください:「Java汎用型消去と型消去によって引き起こされる問題」

それはなん类型擦除ですか?

伪泛型Javaのジェネリック型は、Javaコンパイル中にすべてのジェネリック情報が消去されるためです。Javaのジェネリックは基本的にコンパイラのレベルで実装され、生成されたバイトコードにはジェネリックの型情報が含まれていません。

  • ジェネリックスを使用する場合は、型パラメーターを追加すると、コンパイラーのコンパイル時にパラメーターが削除されます。このプロセスはと呼ばれ类型擦除ます。

たとえば、コードで定義された型はList<String>コンパイル後になります。Listジェネリック型によって付加された型情報はJVMには表示されません。JVMはリストのみを認識します。Javaコンパイラは、コンパイル時に可能な限りエラーを検出しますが、実行時に型変換例外を生成することはできません。型の消去も模板机制JavaのジェネリックとC ++実装重要な違いです。

ワイルドカード

Javaジェネリックのワイルドカードの詳細については、次の記事を確認してください。 JavaジェネリックのワイルドカードT、E、K、Vについて話しましょう。

一般的に使用されるワイルドカードは、T、E、K、V、?

  • ? 不確実なJavaタイプを表します
  • T 特定のJavaタイプ(タイプ)を表します
  • KVJavaキー値それぞれキー値を表します
  • E 要素を表します

ジェネリックを使用する3つの方法

ジェネリック医薬品は、一般的に3つのある泛型类方法:泛型接口泛型方法、。

ジェネリッククラス

これTは任意の識別子として記述できます。T、E、K、Vなどの一般的なパラメーターは、ジェネリックを表すためによく使用されます。

ジェネリッククラスをインスタンス化するときは、特定のタイプのTを指定する必要があります。

public class Generic<T>{
    
     

    private T key;

    public Generic(T key) {
    
     
        this.key = key;
    }

    public T getKey(){
    
     
        return key;
    }
}

ジェネリッククラスをインスタンス化します。

Generic<Integer> genericInteger = new Generic<Integer>(123456);

汎用インターフェース

public interface Generator<T> {
    
    
    public T method();
}
  1. タイプを指定せずに汎用インターフェースを実装します。
class GeneratorImpl<T> implements Generator<T>{
    
    
    @Override
    public T method() {
    
    
        return null;
    }
}
  1. 汎用インターフェースを実装し、タイプを指定します。
class GeneratorImpl<T> implements Generator<String>{
    
    
    @Override
    public String method() {
    
    
        return "hello";
    }
}

一般的な方法

public static <E> void printArray(E[] inputArray){
    
             
    for(E element : inputArray){
    
            
      	System.out.printf( "%s ", element );
    }
    System.out.println();
}

使用する:

// 创建不同类型数组: Integer, Double 和 Character
Integer[] intArray = {
    
     1, 2, 3 };
String[] stringArray = {
    
     "Hello", "World" };
printArray(intArray); 
printArray(stringArray); 

ジェネリック医薬品の利点

ジェネリックを使用する利点は次のとおりです。

  1. コードはより簡潔で、型変換を強制する必要はありません

  2. プログラムはより堅牢で、コンパイル中に警告がなく、実行時にClassCastException例外がありません

  3. 次のようなコードの再利用性を向上させます。動的プロキシでは、ジェネリックスを使用して、プロキシにする必要のあるすべてのクラスをプロキシできます

public class ServiceProxy<T> implements InvocationHandler {
    
    
  	private T target;
  	public ServiceProxy(T target) {
    
    
        this.target = target;
    }
}

一般的なアプリケーション

  1. コレクションを操作する場合

さまざまなタイプのオブジェクトをコンテナに格納できる理由は、ジェネリックスの導入によるものです。

List lists = new ArrayList<String>();
  1. 基本コンポーネントで使用する場合

コンポーネントの要件は、ある程度の汎用性を実現することであり、さまざまな型をサポートする必要があるためです。ジェネリックの特徴は、オブジェクトを作成するとき、またはメソッドを呼び出すときにのみ特定の型が明確になることです。言葉遣いを参照SpringData JPAすることができますJpaRepository

public interface JpaRepository<T, ID> extends 
  PagingAndSortingRepository<T, ID>, QueryExampleExecutor<T> {
    
    
  	List<T> findAll();
  	List<T> findAll(Sort sort);
  	List<T> findAllById(Iterable<ID> ids);
  	<S extends T> List<S> saveAll(Iterable<S> entities);
  	void flush();
   	<S extends T> S saveAndFlush(S entity);
   	void deleteInBatch(Iterable<T> entities);
   	void deleteAllInBatch();
   	T getOne(ID id);
  
   	@Override
   	<S extends T> List<S> findAll(Example<S> example);
  
   	@Override
   	<S extends T> List<S> findAll(Example<S> example, Sort sort);
}

さらに、コンポーネントでは反射机制、一般的にJavaからも分離できません。反射+泛型

別の実用的な例を挙げてください

たとえば、特定のデータベーステーブルの特定のフィールドを集約する必要があり、SQLステートメントは次のようになります。

select sum(column1),sum(column2) from table group by field1,field2

ニーズsumgroup by列は当事者自身のビジネスによって渡されますが、SQLテーブルは実際にはPOJO、POJOの入力フィールドであり、単一のビジネスが実際にPOJOをパラメーターに書き込むことができる属性ですが、パラメーターが汎用に設定されている場合は、改善できますコードの再利用性。

パラメータを取得した後、リフレクションを介してそのフィールドの特定の値を取得すると、累積を実行できます。具体的なコード例は次のとおりです。

// 传入需要 group by 和 sum 的字段名
public cacheMap(List<String> groupByKeys, List<String> sumValues) {
    
    
  	this.groupByKeys = groupByKeys;
  	this.sumValues = sumValues;
}

private void excute(T e) {
    
    
  
    // 从pojo 取出需要group by 的字段 list
    List<Object> key = buildPrimaryKey(e);

    // primaryMap 是存储结果的Map
    T value = primaryMap.get(key);

    // 如果从存储结果找到有相应记录
    if (value != null) {
    
    
        for (String elem : sumValues) {
    
    
          // 反射获取对应的字段,做累加处理
            Field field = getDeclaredField(elem, e);
            if (field.get(e) instanceof Integer) {
    
    
                field.set(value, (Integer) field.get(e) + (Integer) field.get(value));
            } else if (field.get(e) instanceof Long) {
    
    
                field.set(value, (Long) field.get(e) + (Long) field.get(value));
            } else {
    
    
                throw new RuntimeException("类型异常,请处理异常");
            }
      	}
        // 处理时间记录
        Field field = getDeclaredField("updated", value);
        if (null != field) {
    
    
            field.set(value, DateTimeUtils.getCurrentTime());
        }
    } else {
    
    
      	// group by 字段 第一次进来
        try {
    
    
            primaryMap.put(key, Tclone(e));
            createdMap.put(key, DateTimeUtils.getCurrentTime());
        }catch (Exception ex) {
    
    
         	 	log.info("first put value error {}" , e);
        }
    }
}

参照:https//mp.weixin.qq.com/s/fL6cMjSNAX_fZ7wFBJO80Q

おすすめ

転載: blog.csdn.net/weixin_44471490/article/details/111658775