Solution: double dispatch. I replaced:
A result = otherObject.foo();
if (result instanceof B) {
B b = (B)result;
d.doSomething(b);
} else {
C c = (C)result;
d.doSomething(c);
}
with :
otherObject.foo().doSomething(d);
where B and C have different definitions of doSomething.
As desired, otherObject still can't call doSomething, even on Bs and Cs it creates, because it doesn't have access to d.