Недавно я попытался получить доступ к методу clone
типа массива через библиотеку java.lang.invoke
.Это оказалось неудачным.В примере используется метод клонирования, например:
int[] a = new int[]{1, 2, 3, 4};
int[] b = a.clone();
Я хочу создать MethodHandle
для вызова a.clone()
.Таким образом, результирующий код похож на:
int[] a = new int[]{1, 2, 3, 4};
int[] b = findCloneMethod().invoke(a);
Я настроил эту систему для каждого вызова другого метода.Однако система терпит неудачу только с этим методом.
Этот вопрос относится к библиотеке java.lang.invoke
, not * java.lang.reflect
.
Описание проблемы
Следующий пример кода был введен, чтобы показать это поведение.
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
public class Main {
void test() {
final MethodHandles.Lookup caller = MethodHandles.lookup();
final Class<?> refc = int[].class;
final String name = "clone";
final MethodType type = MethodType.methodType(Object.class);
final MethodHandle mh = findVirtual(caller, refc, name, type);
System.out.println("Lookup: " + caller);
System.out.println("mh: " + mh);
}
public static void main(String[] args) {
new Main().test();
}
private MethodHandle findVirtual(final MethodHandles.Lookup caller, final Class<?> refc, final String name, final MethodType type) {
try {
return caller.findVirtual(refc, name, type);
} catch (final Throwable t) {
t.printStackTrace();
return null;
}
}
}
Пример вывода:
Поиск: Главный
mh: MethodHandle (Main) Object
Вывод имеет смысл.Вызывающий объект из области Main
видит только метод clone
в суперклассе Main
: Object
.Даже если int[]
используется в качестве ссылочного класса, этот метод невидим.Это становится проблемой, поскольку, если используется этот сайт вызова, JVM пытается привести int[]
к Main
.
Если ввести ссылочный тип как int[]
, можно ожидать, что метод clone
в типе int[]
общедоступен.Поэтому, когда документация предлагает:
Тип дескриптора метода будет типом метода, с добавленным типом получателя (обычно refc). 1
Что тип mh
будет (int[])Object
.
Попытка решения:
Поскольку Lookup caller
создает помехи для правильного clone
метода, была сделана попытка с использованием открытого поиска:
final MethodHandles.Lookup caller = MethodHandles.publicLookup();
Однако это не удалось.Простая проверка подписи Object#clone()
показывает, почему:
protected Object clone() throws CloneNotSupportedException { ...
Метод protected
.Это не относится к типам массивов, так как реализация делает этот метод общедоступным.
Примером этого может быть попытка доступа к методу Object#equals
.Это показано с тремя отдельными ссылочными классами:
final MethodHandles.Lookup caller = MethodHandles.publicLookup();
final Class<?> refc = Object.class;
final String name = "equals";
final MethodType type = MethodType.methodType(boolean.class, Object.class);
Lookup: java.lang.Object / public
mh: MethodHandle (Object, Object) логический
А затем с использованием Main.class
в качестве базового класса:
final Class<?> refc = Main.class;
Lookup: java.lang.Object / public
mh: MethodHandle (Main, Object)логическое
И затем использование int[].class
в качестве базового класса:
final Class<?> refc = int[].class
Lookup: Main
mh: MethodHandle (int [],Объект) логическое
Это предполагаемое поведение, очевидно, работает бегло.В первом случае ссылочный класс был Object
и возвращал дескриптор метода Object#equals
.Во втором ссылочный класс был Main
и возвращал дескриптор метода Main#equals
.И наконец, эталонный класс был int[].class
и возвращал дескриптор метода int[]#equals
.
Этот паритет не поддерживается с помощью метода int[]#clone
.Поскольку метод clone
защищен, но общедоступен для массивов, его невозможно найти.Мне это кажется ошибкой.Метод массива clone
должен быть общедоступным через publicLookup
.Возвращаемый тип дескриптора метода может быть: MethodHandle(Object)Object
.
После получения MethodHandle#asType
можно затем использовать для исправления типа.В этом случае он будет приведен к MethodHandle(int[])Object
.
Без общедоступного метода Object#clone
, по крайней мере для типов массивов, это кажется невозможным.
Более того, это кажется единственным методом, где это может произойти, так кактипы массивов расширяют / реализуют только 3 класса: Object
, Cloneable
и Serializable
.Единственным другим методом будет Object#finalize
, который также защищен;однако типы массивов не делают этот метод общедоступным.Поэтому clone
- единственный метод, для которого это не удается.
tl; др:
Метод массива clone
, такой как Object[]#clone()
, не доступен для общего доступа через открытый поиск. Это потому что Object#clone
это protected
; однако типы массивов делают этот метод общедоступным. Попытка получить доступ к массиву clone
через ссылочный класс не удалась из-за этой проблемы видимости. В результате для этого метода не может быть сгенерировано MethodHandle
. Тем не менее, использование тех же методов для открытых методов, таких как Object#equals
, прекрасно работает для типов массивов.
Я бы предпочел избежать доступа к отражению, так как существуют методы для его извлечения путем простого изменения уровня доверия поиска.
Есть ли способ сгенерировать MethodHandle
для метода массива clone
?
Примечание: я понимаю правильное использование java.lang.invoke
и не собираюсь использовать его в качестве замены java.lang.reflect
.