7 changed files with 369 additions and 0 deletions
@ -0,0 +1,100 @@ |
|||
问题:Java泛型擦除后如何通过反射获取泛型信息? |
|||
答:虽然 Java 在编译时会进行类型擦除(Type Erasure),将泛型替换为原始类型(通常是 Object 或边界类型),但某些情况下泛型信息会被保留在字节码中,可以通过反射获取: |
|||
|
|||
1. 获取父类/接口的泛型参数 |
|||
Java |
|||
1 |
|||
2 |
|||
3 |
|||
4 |
|||
5 |
|||
6 |
|||
7 |
|||
8 |
|||
9 |
|||
10 |
|||
11 |
|||
12 |
|||
13 |
|||
14 |
|||
15 |
|||
16 |
|||
17 |
|||
18 |
|||
19 |
|||
20 |
|||
21 |
|||
22 |
|||
23 |
|||
import java.lang.reflect.ParameterizedType; |
|||
import java.lang.reflect.Type; |
|||
import java.util.List; |
|||
|
|||
// 继承泛型类时指定具体类型 |
|||
public class StringList extends ArrayList<String> {} |
|||
|
|||
public class GenericReflectionDemo { |
|||
public static void main(String[] args) { |
|||
// 获取父类的泛型参数 |
|||
Class<?> clazz = StringList.class; |
|||
Type genericSuperclass = clazz.getGenericSuperclass(); |
|||
|
|||
if (genericSuperclass instanceof ParameterizedType) { |
|||
ParameterizedType pt = (ParameterizedType) genericSuperclass; |
|||
Type[] actualTypeArgs = pt.getActualTypeArguments(); |
|||
for (Type type : actualTypeArgs) { |
|||
System.out.println("泛型参数类型: " + type); |
|||
// 输出: class java.lang.String |
|||
} |
|||
} |
|||
} |
|||
} |
|||
2. 获取字段的泛型类型 |
|||
Java |
|||
1 |
|||
2 |
|||
3 |
|||
4 |
|||
5 |
|||
6 |
|||
7 |
|||
8 |
|||
9 |
|||
10 |
|||
11 |
|||
12 |
|||
13 |
|||
14 |
|||
public class GenericField { |
|||
private List<String> names; |
|||
private Map<Integer, String> mapping; |
|||
} |
|||
|
|||
// 获取字段泛型 |
|||
Field field = GenericField.class.getDeclaredField("names"); |
|||
Type genericType = field.getGenericType(); |
|||
if (genericType instanceof ParameterizedType) { |
|||
ParameterizedType pt = (ParameterizedType) genericType; |
|||
System.out.println("字段泛型参数: " + |
|||
Arrays.toString(pt.getActualTypeArguments())); |
|||
// 输出: [class java.lang.String] |
|||
} |
|||
3. 获取方法参数的泛型类型 |
|||
Java |
|||
1 |
|||
2 |
|||
3 |
|||
4 |
|||
public void process(List<String> items, Map<K, V> map) {} |
|||
|
|||
Method method = clazz.getMethod("process", List.class, Map.class); |
|||
Type[] paramTypes = method.getGenericParameterTypes(); |
|||
4. 核心 API 总结 |
|||
反射方法 用途 |
|||
getGenericSuperclass() 获取父类的泛型类型 |
|||
getGenericInterfaces() 获取接口的泛型类型 |
|||
Field.getGenericType() 获取字段的泛型类型 |
|||
Method.getGenericReturnType() 获取返回值的泛型类型 |
|||
Method.getGenericParameterTypes() 获取方法参数的泛型类型 |
|||
ParameterizedType.getActualTypeArguments() 获取实际泛型参数 |
|||
⚠️ 限制:局部变量和方法参数的泛型信息在运行时是完全擦除的,无法通过反射获取。 |
|||
@ -0,0 +1,155 @@ |
|||
import java.util.Map; |
|||
import java.util.HashMap; |
|||
import java.util.Set; |
|||
import java.util.Collection; |
|||
import java.util.Optional; |
|||
import java.util.HashSet; |
|||
import java.util.ArrayList; |
|||
|
|||
/** |
|||
* 泛型缓存类 Cache<K, V> |
|||
* 提供简单的键值缓存机制(非线程安全) |
|||
* @param <K> 键的类型 |
|||
* @param <V> 值的类型 |
|||
*/ |
|||
public class Cache<K, V> { |
|||
private final Map<K, V> cache; |
|||
private final int maxSize; |
|||
|
|||
// 统计信息
|
|||
private long hitCount = 0; |
|||
private long missCount = 0; |
|||
|
|||
/** |
|||
* 默认构造方法 - 无容量限制 |
|||
*/ |
|||
public Cache() { |
|||
this(Integer.MAX_VALUE); |
|||
} |
|||
|
|||
/** |
|||
* 带容量限制的构造方法 |
|||
*/ |
|||
public Cache(int maxSize) { |
|||
this.maxSize = maxSize; |
|||
this.cache = new HashMap<>(); |
|||
} |
|||
|
|||
/** |
|||
* 存入缓存 |
|||
*/ |
|||
public void put(K key, V value) { |
|||
if (key == null) { |
|||
throw new IllegalArgumentException("Key cannot be null"); |
|||
} |
|||
if (cache.size() >= maxSize && !cache.containsKey(key)) { |
|||
throw new IllegalStateException("Cache is full, no eviction strategy implemented"); |
|||
} |
|||
cache.put(key, value); |
|||
} |
|||
|
|||
/** |
|||
* 从缓存获取 |
|||
*/ |
|||
public V get(K key) { |
|||
if (cache.containsKey(key)) { |
|||
hitCount++; |
|||
return cache.get(key); |
|||
} else { |
|||
missCount++; |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 使用 Optional 包装返回值(更安全的API) |
|||
*/ |
|||
public Optional<V> getOptional(K key) { |
|||
return Optional.ofNullable(get(key)); |
|||
} |
|||
|
|||
/** |
|||
* 判断缓存中是否存在键 |
|||
*/ |
|||
public boolean containsKey(K key) { |
|||
return cache.containsKey(key); |
|||
} |
|||
|
|||
/** |
|||
* 从缓存中移除 |
|||
*/ |
|||
public V remove(K key) { |
|||
return cache.remove(key); |
|||
} |
|||
|
|||
/** |
|||
* 清空缓存 |
|||
*/ |
|||
public void clear() { |
|||
cache.clear(); |
|||
hitCount = 0; |
|||
missCount = 0; |
|||
} |
|||
|
|||
/** |
|||
* 获取缓存大小 |
|||
*/ |
|||
public int size() { |
|||
return cache.size(); |
|||
} |
|||
|
|||
/** |
|||
* 判断缓存是否为空 |
|||
*/ |
|||
public boolean isEmpty() { |
|||
return cache.isEmpty(); |
|||
} |
|||
|
|||
/** |
|||
* 获取所有键 |
|||
*/ |
|||
public Set<K> keySet() { |
|||
return new HashSet<>(cache.keySet()); |
|||
} |
|||
|
|||
/** |
|||
* 获取所有值 |
|||
*/ |
|||
public Collection<V> values() { |
|||
return new ArrayList<>(cache.values()); |
|||
} |
|||
|
|||
/** |
|||
* 获取命中率 |
|||
*/ |
|||
public double getHitRate() { |
|||
long total = hitCount + missCount; |
|||
return total == 0 ? 0.0 : (double) hitCount / total; |
|||
} |
|||
|
|||
/** |
|||
* 获取统计信息 |
|||
*/ |
|||
public CacheStats getStats() { |
|||
return new CacheStats(hitCount, missCount, getHitRate()); |
|||
} |
|||
|
|||
// 统计信息内部类
|
|||
public static class CacheStats { |
|||
public final long hits; |
|||
public final long misses; |
|||
public final double hitRate; |
|||
|
|||
public CacheStats(long hits, long misses, double hitRate) { |
|||
this.hits = hits; |
|||
this.misses = misses; |
|||
this.hitRate = hitRate; |
|||
} |
|||
|
|||
@Override |
|||
public String toString() { |
|||
return String.format("CacheStats{hits=%d, misses=%d, hitRate=%.2f%%}", |
|||
hits, misses, hitRate * 100); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
public class CacheTest { |
|||
public static void main(String[] args) { |
|||
// 创建缓存实例
|
|||
Cache<String, Integer> ageCache = new Cache<>(100); |
|||
|
|||
// 存入数据
|
|||
ageCache.put("Alice", 25); |
|||
ageCache.put("Bob", 30); |
|||
ageCache.put("Charlie", 35); |
|||
|
|||
// 获取数据
|
|||
System.out.println("Alice's age: " + ageCache.get("Alice")); |
|||
|
|||
// 测试缓存未命中
|
|||
Integer davidAge = ageCache.get("David"); |
|||
System.out.println("David's age: " + davidAge); |
|||
|
|||
// 查看统计信息
|
|||
System.out.println(ageCache.getStats()); |
|||
|
|||
// 使用 Optional API
|
|||
ageCache.getOptional("Eve").ifPresentOrElse( |
|||
age -> System.out.println("Found: " + age), |
|||
() -> System.out.println("Eve not found") |
|||
); |
|||
} |
|||
} |
|||
@ -0,0 +1,67 @@ |
|||
/** |
|||
* 泛型 Pair 类 - 用于存储键值对 |
|||
* @param <K> 键的类型 |
|||
* @param <V> 值的类型 |
|||
*/ |
|||
public class Pair<K, V> { |
|||
private K key; |
|||
private V value; |
|||
|
|||
// 构造方法
|
|||
public Pair(K key, V value) { |
|||
this.key = key; |
|||
this.value = value; |
|||
} |
|||
|
|||
// Getter 方法
|
|||
public K getKey() { |
|||
return key; |
|||
} |
|||
|
|||
public V getValue() { |
|||
return value; |
|||
} |
|||
|
|||
// Setter 方法
|
|||
public void setKey(K key) { |
|||
this.key = key; |
|||
} |
|||
|
|||
public void setValue(V value) { |
|||
this.value = value; |
|||
} |
|||
|
|||
/** |
|||
* swap 方法 - 交换当前 Pair 的 key 和 value |
|||
* 注意:只有当 K 和 V 类型相同时才能交换,否则返回新 Pair |
|||
*/ |
|||
public Pair<V, K> swap() { |
|||
return new Pair<>(value, key); |
|||
} |
|||
|
|||
/** |
|||
* 静态泛型方法 - 交换任意两个 Pair 对象 |
|||
*/ |
|||
public static <K, V> Pair<V, K> swap(Pair<K, V> pair) { |
|||
return new Pair<>(pair.getValue(), pair.getKey()); |
|||
} |
|||
|
|||
@Override |
|||
public String toString() { |
|||
return "Pair{" + "key=" + key + ", value=" + value + '}'; |
|||
} |
|||
|
|||
@Override |
|||
public boolean equals(Object obj) { |
|||
if (this == obj) return true; |
|||
if (obj == null || getClass() != obj.getClass()) return false; |
|||
Pair<?, ?> pair = (Pair<?, ?>) obj; |
|||
return java.util.Objects.equals(key, pair.key) && |
|||
java.util.Objects.equals(value, pair.value); |
|||
} |
|||
|
|||
@Override |
|||
public int hashCode() { |
|||
return java.util.Objects.hash(key, value); |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
public class PairTest { |
|||
public static void main(String[] args) { |
|||
// 测试整数-字符串 Pair
|
|||
Pair<Integer, String> pair1 = new Pair<>(1, "One"); |
|||
System.out.println("原 Pair: " + pair1); |
|||
|
|||
// 使用实例方法 swap
|
|||
Pair<String, Integer> swapped = pair1.swap(); |
|||
System.out.println("交换后: " + swapped); |
|||
|
|||
// 使用静态方法 swap
|
|||
Pair<String, Integer> swapped2 = Pair.swap(pair1); |
|||
System.out.println("静态交换: " + swapped2); |
|||
|
|||
// 测试相同类型的 Pair 交换
|
|||
Pair<String, String> pair2 = new Pair<>("A", "B"); |
|||
Pair<String, String> swappedSame = pair2.swap(); |
|||
System.out.println("同类型交换: " + swappedSame); |
|||
} |
|||
} |
|||
|
After Width: | Height: | Size: 7.1 KiB |
|
After Width: | Height: | Size: 8.8 KiB |
Loading…
Reference in new issue