如何使用Java8 Supplier接口重写工厂方法以提供正确类型实例?
使用Java 8 Supplier重构Thingy工厂方法
嘿,这个问题我刚好琢磨过,咱们一步步把它搞定!原来的工厂用反射创建实例不仅有运行时风险,还得加一堆unchecked警告,换成Supplier之后不仅代码更清爽,类型安全也更有保障。
第一步:调整工厂的存储结构
原来你用Map<String, Class<Thingy<?, ?>>>来存实现类,现在咱们把它换成存Supplier实例——毕竟Supplier的核心就是提供实例,完全贴合工厂的需求:
public final class ThingyFactory { // 替换原来的thingyclasses,用Supplier存储实例提供者 private final Map<String, Supplier<? extends Thingy<?, ?>>> thingySuppliers = new ConcurrentHashMap<>(); // ... 其他代码 }
第二步:实现注册方法
现在注册Thingy实现类的时候,不用传Class对象了,直接传一个Supplier。你可以用方法引用(比如ConcreteThingy::new)来快速创建Supplier,非常方便:
// 泛型化的注册方法,保证类型一致性 public <K, V> void registerThingy(String key, Supplier<Thingy<K, V>> supplier) { thingySuppliers.put(key, supplier); }
举个注册的例子,假设你有个ConcreteThingy实现了Thingy<String, Integer>:
factory.registerThingy("string-int", ConcreteThingy::new);
第三步:重构获取实例的方法
原来的反射创建逻辑可以直接替换成调用Supplier的get()方法,虽然还是需要一个unchecked警告,但相比反射的场景,这个警告的风险要小得多——毕竟注册时已经绑定了正确的构造逻辑:
@SuppressWarnings("unchecked") // 仅在必要时保留,比反射更安全 public <K, V> Thingy<K, V> getThingy(String key) { Supplier<? extends Thingy<?, ?>> supplier = thingySuppliers.get(key); if (supplier == null) { throw new IllegalArgumentException("找不到对应key的Thingy实现:" + key); } // 这里的类型转换是安全的,因为注册时我们已经确保Supplier返回对应类型 return (Thingy<K, V>) supplier.get(); }
完整示例代码
咱们把所有部分拼起来,看看实际运行效果:
Thingy接口
public interface Thingy<K, V> extends Map<K, V> {}
具体实现类
public class ConcreteThingy<K, V> implements Thingy<K, V> { // 用HashMap做委托,简化Map方法的实现 private final Map<K, V> delegate = new HashMap<>(); @Override public int size() { return delegate.size(); } @Override public boolean isEmpty() { return delegate.isEmpty(); } @Override public boolean containsKey(Object key) { return delegate.containsKey(key); } @Override public boolean containsValue(Object value) { return delegate.containsValue(value); } @Override public V get(Object key) { return delegate.get(key); } @Override public V put(K key, V value) { return delegate.put(key, value); } @Override public V remove(Object key) { return delegate.remove(key); } @Override public void putAll(Map<? extends K, ? extends V> m) { delegate.putAll(m); } @Override public void clear() { delegate.clear(); } @Override public Set<K> keySet() { return delegate.keySet(); } @Override public Collection<V> values() { return delegate.values(); } @Override public Set<Entry<K, V>> entrySet() { return delegate.entrySet(); } }
重构后的ThingyFactory
import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Supplier; public final class ThingyFactory { private final Map<String, Supplier<? extends Thingy<?, ?>>> thingySuppliers = new ConcurrentHashMap<>(); public <K, V> void registerThingy(String key, Supplier<Thingy<K, V>> supplier) { thingySuppliers.put(key, supplier); } @SuppressWarnings("unchecked") public <K, V> Thingy<K, V> getThingy(String key) { Supplier<? extends Thingy<?, ?>> supplier = thingySuppliers.get(key); if (supplier == null) { throw new IllegalArgumentException("No Thingy registered for key: " + key); } return (Thingy<K, V>) supplier.get(); } // 测试代码 public static void main(String[] args) { ThingyFactory factory = new ThingyFactory(); factory.registerThingy("string-int", ConcreteThingy::new); Thingy<String, Integer> myThingy = factory.getThingy("string-int"); myThingy.put("answer", 42); System.out.println("The answer is: " + myThingy.get("answer")); // 输出42 } }
为什么这么做更好?
- 避免反射风险:不用再处理
InstantiationException、IllegalAccessException这些运行时异常,注册时如果构造方法有问题,编译阶段就会报错。 - 代码更简洁:方法引用
ConcreteThingy::new比反射代码可读性高太多。 - 类型更安全:注册时就绑定了正确的泛型类型,相比反射的强制转换,风险大幅降低。
内容的提问来源于stack exchange,提问作者Richard Sand
相关产品推荐
相关产品推荐

