I'm curious as to how instance testing (instanceof/is/using dynamic_cast in c++) works
In Java, each object stores a pointer to its class. Once we have that, we can enumerate each ancestor type in the digraph of types. x instanceof T iff we encounter T during the search.
Also, it's been drilled into my head that using instance testing is a mark of bad design. Why exactly is this? When is that applicable, instanceof should still be used in methods like .equals() and such right?
The idea is that, when possible, it's preferable to code to use polymorphism rather than checking for specific subtypes. It tends to be more elegant. Also, there's no possibility of forgetting to update our subtype checks when we add a new subtype.
But I agree, instanceof is still appropriate in equals since the API forces us to deal with Objects.
When you have mutliple catch statements, how does that work? Is that instance testing or is it just resolved during compilation where each thrown exception would go to?
In Java, it's not possible for a compiler to precompute each exception's path because new exception classes could be loaded at runtime.
The compiler does generate an "exception table" for each try/catch block, where a row in the table contains an exception type and a pc offset which should be applied if that exception is encountered. But if you declare catch (IOException), the exception table will only contain IOException, not subclasses like EOFException. An instanceof-like check has to be done at runtime to check if a thrown exception is an IOException.