Любой класс должен иметь загрузчик классов, поэтому мы должны дать его здесь.
Важной частью является следующее (в документации для getProxyClass()
):
Все типы интерфейсов должны быть видны по имени через указанный загрузчик классов. Другими словами,
для загрузчика классов cl и каждого интерфейса i следующее выражение должно быть истинным:
Class.forName(i.getName(), false, cl) == i
Таким образом, вы можете использовать любой загрузчик классов, где один (или более) из его родительских загрузчиков классов определил данные интерфейсы.
Если null
работает в вашем случае, я полагаю, что ваши интерфейсы также имеют загрузчик классов null
(загрузчик начальной загрузки) - тогда не должно иметь значения, какой загрузчик классов вы использовали. Если вам нужно создать прокси из неизвестных вам интерфейсов, просто возьмите загрузчик классов с первым данным интерфейсом и надеемся, что ваш вызывающий не сделал странного.
Зачем это нужно?
Вы можете представить это так:
- Метод
getProxyClass()
создает (если он еще не существует) некоторый байт-код для нового класса, реализующего все методы всего вашего интерфейса (каждый из них просто перенаправляет вызов вашему InvocationHandler
).
- Затем он передает этот байт-код методу
defineClass
указанного вами загрузчика классов.
- В этом байт-коде все ваши интерфейсы имеют ссылки по именам, и виртуальная машина теперь использует цитированный
forName
вызов для разрешения этих интерфейсов.
Мы могли бы реализовать это getProxyClass
таким образом в чистой Java без какой-либо магии виртуальной машины, но нам нужно было бы создать для нее новый загрузчик классов (с указанным в качестве родительского) вместо возможности повторного использования существующего. .
На самом деле для этого синтетического класса может не существовать фактического байт-кода, поскольку виртуальная машина может использовать здесь свою внутреннюю магию: -)