Java反射调用W32FileUtils的moveToTrash方法时参数类型不匹配
问题
因跨平台需求(仅Windows版本需JNA类,macOS因公证规则无需),通过反射访问JNA的com.sun.jna.platform.win32.W32FileUtils类。目前可成功获取实例并调用hasTrash()方法,但调用moveToTrash(File[])方法时,抛出java.lang.IllegalArgumentException: argument type mismatch异常。已传入File[]类型参数,无法理解为何出现类型不匹配,相关栈信息如下:
java.lang.IllegalArgumentException: argument type mismatch at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:107) at java.base/java.lang.reflect.Method.invoke(Method.java:580) at com.jthink.songkong.analyse.duplicates.WindowsDeleteTrash.deleteTrash(WindowsDeleteTrash.java:44)
注:getInstance()返回的是FileUtils类型,W32FileUtils是其子类,但hasTrash()调用正常。
相关代码如下:
W32FileUtils类代码
package com.sun.jna.platform.win32; import com.sun.jna.WString; import com.sun.jna.platform.FileUtils; import java.io.File; import java.io.IOException; public class W32FileUtils extends FileUtils { public W32FileUtils() { } public boolean hasTrash() { return true; } public void moveToTrash(File[] files) throws IOException { Shell32 shell = Shell32.INSTANCE; ShellAPI.SHFILEOPSTRUCT fileop = new ShellAPI.SHFILEOPSTRUCT(); fileop.wFunc = 3; String[] paths = new String[files.length]; int ret; for(ret = 0; ret < paths.length; ++ret) { paths[ret] = files[ret].getAbsolutePath(); } fileop.pFrom = new WString(fileop.encodePaths(paths)); fileop.fFlags = 1620; ret = shell.SHFileOperation(fileop); if (ret != 0) { throw new IOException("Move to trash failed: " + fileop.pFrom + ": " + Kernel32Util.formatMessageFromLastErrorCode(ret)); } else if (fileop.fAnyOperationsAborted) { throw new IOException("Move to trash aborted"); } } }
WindowsDeleteTrash类代码
package com.jthink.songkong.analyse.duplicates; import com.jthink.songkong.text.InfoMessage; import com.jthink.songkong.ui.MainWindow; import java.io.File; import java.lang.reflect.Method; import java.util.logging.Level; public class WindowsDeleteTrash { public boolean deleteTrash( File file) { Boolean isHasTrash=false; boolean result = false; Object instance = null; Class<?> windowsFileUtilsClass = null; try { windowsFileUtilsClass = Class.forName("com.sun.jna.platform.win32.W32FileUtils"); Method instanceClassMethod = windowsFileUtilsClass.getMethod("getInstance"); instance = instanceClassMethod.invoke(null); Method instanceHasTrashMethod = windowsFileUtilsClass.getMethod("hasTrash"); isHasTrash = (Boolean) instanceHasTrashMethod.invoke(instance, null); } catch(Exception ex) { MainWindow.logger.log(Level.SEVERE, ex.getMessage(), ex); return file.delete(); } if (isHasTrash) { try { File[] files = new File[1]; files[0] = file; Method instanceMoveMethod = windowsFileUtilsClass.getMethod( "moveToTrash", java.io.File[].class); instanceMoveMethod.invoke(instance, files); result = true; } //SONGKONG-1559 catch (java.lang.NoClassDefFoundError ncde) { MainWindow.logger.log(Level.SEVERE, InfoMessage.MSG_UNABLE_TO_MOVE_TO_TRASH.getMsg() + ncde.getMessage(), ncde); result = file.delete(); } //SONGKONG-1090 catch(Exception ex) { MainWindow.logger.log(Level.SEVERE, ex.getMessage(), ex); result = file.delete(); } } else { result = file.delete(); } return result; } }
解决方法
这个异常的核心原因是反射调用数组类型参数方法时的参数传递逻辑问题:Method.invoke()的第二个参数是可变参数Object... args,当直接传入File[]时,JVM会将数组拆分为多个独立参数传递,但moveToTrash仅接受一个File[]类型参数,因此触发类型不匹配。
修复代码
将数组包装为Object[]传入,避免JVM拆分数组:
// 原代码 instanceMoveMethod.invoke(instance, files); // 修改后代码 instanceMoveMethod.invoke(instance, new Object[]{files});
原理说明
把File[]放到Object[]中后,JVM会将整个File[]作为单个参数传递给目标方法,完全匹配moveToTrash(File[])的参数类型要求。也可以用instanceMoveMethod.invoke(instance, (Object) files)实现相同效果,强制将数组转为单个Object参数。
内容的提问来源于stack exchange,提问作者Paul Taylor
相关产品推荐
相关产品推荐

