用 ? super t 构建多级日志追加器的核心是利用下界通配符实现安全写入:通过 log 继承体系(如 errorlog/warnlog/infolog 均继承自 abstract class log),声明 list

用 ? super T 构建多级日志追加器,核心是让不同子类型的日志对象(如 ErrorLog、WarnLog、InfoLog)都能安全写入一个统一的父类型容器(如 List<log></log> 或 List<object></object>),同时不破坏类型安全。关键不在“多级”本身,而在于利用下界通配符的写入弹性,配合合理的日志类继承结构。
明确日志类的继承关系
确保所有具体日志类型都继承自一个公共基类或实现同一接口,例如:
-
abstract class Log作为顶层抽象基类 class ErrorLog extends Logclass WarnLog extends Logclass InfoLog extends Log
这样,List super Log> 就能接受 List<log></log>、List<object></object>,甚至 List<serializable></serializable> 等更宽泛的容器——只要它们能容纳 Log 及其子类实例。
定义泛型追加方法,只写入、不读取
设计一个通用追加入口,参数声明为 List super Log>,体现“消费者”语义:
public static void appendLogs(List super Log> target, List extends Log> source) {
target.addAll(source); // ✅ 安全:source 中每个元素都是 Log 或其子类,target 能装下
}
注意两点:
- 目标容器用
? super Log→ 支持写入 - 源数据用
? extends Log→ 支持读取(因 source 是只读提供方) - 避免在方法体内对
target.get(i)做强转(如(ErrorLog) target.get(0)),那会编译失败;若真需读,应限定为Object或上溯到已知共同父类
实战中推荐的容器类型选择
不要直接用 List<object></object> —— 虽然它满足 ? super Log,但丧失了日志语义约束。更合理的是:
- 主日志容器声明为
List<log></log>(最常用、语义清晰) - 配置中心或跨模块日志聚合器可声明为
List super Log>,以便接收来自不同模块的List<errorlog></errorlog>、List<infolog></infolog>等输入 - 避免使用
List super Object>—— 它等价于List<object></object>,失去泛型意义
规避常见误用
以下写法看似灵活,实则危险或无效:
-
❌ List super ErrorLog> logs = new ArrayList
(); logs.add(new InfoLog()); → 编译失败,因为InfoLog不是ErrorLog的子类 -
❌ Log log = logs.get(0); → 编译错误,
get()返回Object,必须显式转为Log才能用(且需运行时检查) - ✅ 正确姿势:Object raw = logs.get(0); if (raw instanceof Log) { process((Log) raw); }










