didi/DoKit · error · ReflectedException

Caller [" + caller + "] is not a instance of type [" + mType

Error message

Caller [" + caller + "] is not a instance of type [" + mType + "]!

What it means

Reflector.checked(Object caller) validates that the caller object is an instance of the Reflector's bound type (mType). If caller is non-null but not an mType instance, it throws ReflectedException with the message 'Caller [...] is not a instance of type [...]!'. It guards against invoking members on an object of the wrong class.

Source

Thrown at Android/dokit/src/main/java/com/didichuxing/doraemonkit/util/Reflector.java:96

    @SuppressWarnings("unchecked")
    public <R> R newInstance(@Nullable Object ... initargs) throws ReflectedException {
        if (mConstructor == null) {
            throw new ReflectedException("Constructor was null!");
        }
        try {
            return (R) mConstructor.newInstance(initargs);
        } catch (InvocationTargetException e) {
            throw new ReflectedException("Oops!", e.getTargetException());
        } catch (Throwable e) {
            throw new ReflectedException("Oops!", e);
        }
    }
    
    protected Object checked(@Nullable Object caller) throws ReflectedException {
        if (caller == null || mType.isInstance(caller)) {
            return caller;
        }
        throw new ReflectedException("Caller [" + caller + "] is not a instance of type [" + mType + "]!");
    }
    
    protected void check(@Nullable Object caller, @Nullable Member member, @NonNull String name) throws ReflectedException {
        if (member == null) {
            throw new ReflectedException(name + " was null!");
        }
        if (caller == null && !Modifier.isStatic(member.getModifiers())) {
            throw new ReflectedException("Need a caller!");
        }
        checked(caller);
    }
    
    public Reflector bind(@Nullable Object caller) throws ReflectedException {
        mCaller = checked(caller);
        return this;
    }
    
    public Reflector unbind() {

View on GitHub (pinned to 626827cddb)

Solutions

  1. Check the message — it prints both the caller's toString and the expected mType; reconcile which class actually declares the member
  2. Create the Reflector from the class that actually declares the member (walk to the declaring superclass via findField) or pass a caller of that type
  3. If classloader duplication is suspected, compare caller.getClass() and mType and their classloaders before calling

Example fix

// before
Reflector.on(View.class).field("mAttachInfo").get(someViewGroup); // ok if subclass
Reflector.on(TextView.class).field("mLayout").get(someImageView); // throws

// after
Reflector.on(TextView.class).field("mLayout").get((TextView) someView);
Defensive patterns

Strategy: type-guard

Validate before calling

if (caller != null && !mType.isInstance(caller)) {
    throw new IllegalArgumentException(caller.getClass() + " is not " + mType);
}

Type guard

static boolean isInstanceOf(Object caller, Class<?> type) {
    return caller == null || type.isInstance(caller);
}

Try / catch

try {
    r.get(caller);
} catch (ReflectedException e) {
    if (e.getMessage() != null && e.getMessage().startsWith("Caller [")) { /* wrong caller class */ }
}

Prevention

When it happens

Trigger: Calling get(caller)/set(caller,...)/callByCaller(caller,...) with an object whose class differs from the class the Reflector was created with; using Reflector.on(A.class) but passing a B instance because the code assumed inheritance that does not exist; passing a subclass when the reflector was bound to an unrelated interface.

Common situations: Refactoring where a reflected field lookup was copied to operate on a different view/fragment class; mixing classloaders so isInstance fails despite identical class names; vendor-specific class hierarchies diverging from AOSP.

Related errors


AI-assisted analysis of didi/DoKit@626827cddb (2026-08-14). Data as JSON: /api/errors/a8e7d4c3c1e13a73. Report an issue: GitHub.