ジェネリックス入門
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タイプ(タイプ)を表しますK
、V
Javaキー値でそれぞれキー値を表します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();
}
- タイプを指定せずに汎用インターフェースを実装します。
class GeneratorImpl<T> implements Generator<T>{
@Override
public T method() {
return null;
}
}
- 汎用インターフェースを実装し、タイプを指定します。
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);
ジェネリック医薬品の利点
ジェネリックを使用する利点は次のとおりです。
-
コードはより簡潔で、型変換を強制する必要はありません
-
プログラムはより堅牢で、コンパイル中に警告がなく、実行時に
ClassCastException
例外がありません -
次のようなコードの再利用性を向上させます。動的プロキシでは、ジェネリックスを使用して、プロキシにする必要のあるすべてのクラスをプロキシできます
public class ServiceProxy<T> implements InvocationHandler {
private T target;
public ServiceProxy(T target) {
this.target = target;
}
}
一般的なアプリケーション
- コレクションを操作する場合
さまざまなタイプのオブジェクトをコンテナに格納できる理由は、ジェネリックスの導入によるものです。
List lists = new ArrayList<String>();
- 基本コンポーネントで使用する場合
コンポーネントの要件は、ある程度の汎用性を実現することであり、さまざまな型をサポートする必要があるためです。ジェネリックの特徴は、オブジェクトを作成するとき、またはメソッドを呼び出すときにのみ特定の型が明確になることです。言葉遣いを参照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
ニーズsum
とgroup 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);
}
}
}