{"record":{"id":"1175d1c1961eec19","repo":"java-native-access/jna","slug":"callback-must-implement-a-single-public-method-or-one-public","errorCode":null,"errorMessage":"Callback must implement a single public method, or one public method named 'callback'","messagePattern":"Callback must implement a single public method, or one public method named 'callback'","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"src/com/sun/jna/CallbackReference.java","lineNumber":427,"sourceCode":"            Method m = i.next();\n            if (Callback.FORBIDDEN_NAMES.contains(m.getName())) {\n                i.remove();\n            }\n        }\n\n        Method[] methods = pmethods.toArray(new Method[0]);\n        if (methods.length == 1) {\n            return checkMethod(methods[0]);\n        }\n        for (int i=0;i < methods.length;i++) {\n            Method m = methods[i];\n            if (Callback.METHOD_NAME.equals(m.getName())) {\n                return checkMethod(m);\n            }\n        }\n        String msg = \"Callback must implement a single public method, \"\n            + \"or one public method named '\" + Callback.METHOD_NAME + \"'\";\n        throw new IllegalArgumentException(msg);\n    }\n\n    /** Set the behavioral options for this callback. */\n    private void setCallbackOptions(int options) {\n        cbstruct.setInt(Native.POINTER_SIZE, options);\n    }\n\n    /** Obtain a pointer to the native glue code for this callback. */\n    public Pointer getTrampoline() {\n        if (trampoline == null) {\n            trampoline = cbstruct.getPointer(0);\n        }\n        return trampoline;\n    }\n\n    /** Free native resources associated with this callback. */\n    public void close() {\n        if (cleanable != null) {","sourceCodeStart":409,"sourceCodeEnd":445,"githubUrl":"https://github.com/java-native-access/jna/blob/d036ad9781adad4b66693e8fa7098e4ac665e0a3/src/com/sun/jna/CallbackReference.java#L409-L445","documentation":"IllegalArgumentException thrown by CallbackReference.getCallbackMethod when it cannot determine which method of the callback interface is the native callback entry point. JNA requires the interface to declare exactly one public method, or (if multiple) one named 'callback' (Callback.METHOD_NAME). Otherwise the mapping from Java method to native trampoline is ambiguous and registration fails.","triggerScenarios":"Registering a callback interface that: declares zero public methods; declares multiple public methods with none named 'callback'; implements several inherited public methods (e.g. extends two interfaces each adding a public method); relies on a method named something other than 'callback' while other public methods exist (e.g. toString-style helpers, Comparable-style methods inherited).","commonSituations":"Callback interfaces extending other interfaces (adding public default/abstract methods) so more than one public method exists; adding a second helper method to a working single-method callback; lambdas/functions objects whose interface has multiple public members.","solutions":["Keep callback interfaces to exactly one abstract public method (a functional-interface shape), or","If multiple methods exist, name the native entry point 'callback' (void callback(Object[] args) style) so JNA can disambiguate.","Move helper/default methods out of the interface or make them non-public/static; verify with 'javap' or IDE outline that only one public method (or one named 'callback') remains."],"exampleFix":"// before\ninterface Cb extends Callback {\n  void invoke(int code);\n  void log(String s); // second public method -> ambiguous\n}\n// after\ninterface Cb extends Callback { void invoke(int code); }\n// or\ninterface Cb2 extends Callback { void callback(Object[] args); }","handlingStrategy":"validation","validationCode":"static void checkSinglePublicMethod(Class<?> cbType) {\n    long count = java.util.Arrays.stream(cbType.getMethods())\n        .filter(m -> !m.isSynthetic() && !m.getDeclaringClass().equals(Object.class))\n        .filter(m -> m.getName().equals(Callback.METHOD_NAME))\n        .count();\n    // require exactly one 'callback' method or a single public method total\n}","typeGuard":"static boolean hasDisambiguatedCallback(Class<?> t) {\n    Method[] ms = t.getMethods();\n    long named = Arrays.stream(ms).filter(m -> m.getName().equals(Callback.METHOD_NAME)).count();\n    return named == 1 || Arrays.stream(ms)\n        .filter(m -> m.getDeclaringClass() != Object.class && !m.isSynthetic()).count() == 1;\n}","tryCatchPattern":"try { registerCallback(cb); } catch (IllegalArgumentException e) { if (e.getMessage().contains(\"single public method\")) { /* reduce to one method or rename to 'callback' */ } throw e; }","preventionTips":["Keep callback interfaces functional (one abstract method).","Name the entry method 'callback' when multiple public methods exist.","Avoid extending extra interfaces that add public methods to callbacks.","Add a reflective unit test validating each callback interface's shape."],"tags":["jna","callback","ambiguous-method","interface-design"],"backgroundTag":"invalid-argument-value","analyzedSha":"d036ad9781adad4b66693e8fa7098e4ac665e0a3","analyzedAt":"2026-09-12T06:50:59.239Z","contentChangedAt":"2026-09-12T06:50:59.239Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}