为什么两个同名的类型不能互相替代?为什么会有 LinkageError: loader constraint violation?这篇用自己写的类加载器把这些错误一个个复现出来,并看清它们的触发条件和两种不同的错误信息。
同一个 class 文件、两个不同的类加载器各自加载 lib.Dep:d1 == d2 → false,名字相同 → true,d1 的加载器 = A,d2 的加载器 = B
d2.isInstance(o1) = false;d1.isInstance(o1) = true;d2.isAssignableFrom(d1) = false
d2.cast(o1) → ClassCastException: Cannot cast lib.Dep to lib.Dep
同一个加载器第二次 defineClass 同名类 → LinkageError: loader 'A' @70dea4e attempted duplicate class definition for lib.Dep. (lib.Dep is in unnamed module of loader 'A' @70dea4e, parent loader 'bootstrap')
两个都委派给引导加载器的 java.lang.String:s1 == s2 → true(JDK 核心类只有一份)
l1 与 l2 的 parent 都是 null(引导加载器);Class.forName("lib.Dep", false, l1) 与 l1.loadClass 返回同一个对象:trueCaller 的加载器 = C;C 里的 lib.Dep 的加载器 = C;Callee 的加载器 = P;P 里的 lib.Dep 的加载器 = P 调用 Caller.go()(里面是 Callee.m(new Dep()),两个 Dep 来自不同加载器)→ LinkageError: loader constraint violation: when resolving method 'java.lang.String lib.Callee.m(lib.Dep)' the class loader 'C' @33909752 of the current class, app/Caller, and the class loader 'P' @70dea4e for the method's defining class, lib/Callee, have different Class objects for the type lib/Dep used in the signature (app.Caller is in unna… 对照:C2 只自己定义 Caller,Dep 与 Callee 都委派给 P → Caller.go() = Callee.m 收到 Dep@P
第 1 次调用 → LinkageError: loader constraint violation: loader 'P' @70dea4e wants to load class lib.Dep. A different class with the same name was previously loaded by 'C' @33909752. (lib.Dep is in unnamed module of loader 'C' @33909752, parent loader 'P' @70dea4e) 第 2 次调用 → LinkageError: loader constraint violation: loader 'P' @70dea4e wants to load class lib.Dep. A different class with the same name was previously loaded by 'C' @33909752. (lib.Dep is in unnamed module of loader 'C' @33909752, parent loader 'P' @70dea4e) 之后让 P 自己加载它的 lib.Dep → LinkageError: loader constraint violation: loader 'P' @70dea4e wants to load class lib.Dep. A different class with the same name was previously loaded by 'C' @33909752. (lib.Dep is in unnamed module of loader 'C' @33909752, parent loader 'P' @70dea4e)(P 自己的 Dep 也加载不了了) P 加载 lib.Callee(不涉及 Dep)→ 成功:lib.Callee 共享的接口(java.util.List)在两个加载器里是同一个 Class:true——跨加载器通信要靠双方都能看到的父加载器里的类型